home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 23 / CU Amiga - Super CD-ROM 23 (June 1998).iso / CreatingGames / GameCreators / Inform / library / parserm.h < prev    next >
Encoding:
Text File  |  1997-03-15  |  157.9 KB  |  4,623 lines

  1. ! ----------------------------------------------------------------------------
  2. !  PARSERM:  Core of parser.
  3. !
  4. !  Supplied for use with Inform 6                         Serial number 970918
  5. !                                                                  Release 6/7
  6. !  (c) Graham Nelson 1993, 1994, 1995, 1996, 1997
  7. !      but freely usable (see manuals)
  8. ! ----------------------------------------------------------------------------
  9. !  Inclusion of "linklpa"
  10. !                   (which defines properties and attributes)
  11. !  Global variables, constants and arrays
  12. !                1: outside of the parser
  13. !                2: used within the parser
  14. !  Inclusion of natural language definition file
  15. !                   (which creates a compass and direction-objects)
  16. !  Darkness and player objects
  17. !  Definition of grammar token numbering system used by Inform
  18. !
  19. !  The InformParser object
  20. !          keyboard reading
  21. !          level 0: outer shell, conversation, errors
  22. !                1: grammar lines
  23. !                2: tokens
  24. !                3: object lists
  25. !                4: scope and ambiguity resolving
  26. !                5: object comparisons
  27. !                6: word comparisons
  28. !                7: reading words and moving tables about
  29. !          pronoun management
  30. !
  31. !  The InformLibrary object
  32. !          main game loop
  33. !          action processing
  34. !          end of turn sequence
  35. !          scope looping, before/after sequence, sending messages out
  36. !          timers, daemons, time of day, score notification
  37. !          light and darkness
  38. !          changing player personality
  39. !          tracing code (only present if DEBUG is set)
  40. !
  41. !  Status line printing, menu display
  42. !  Printing object names with articles
  43. !  Miscellaneous utility routines
  44. !  Game banner, "version" verb, run-time errors
  45. ! ----------------------------------------------------------------------------
  46.  
  47. System_file;
  48. Constant NULL = $ffff;
  49.  
  50. IFDEF MODULE_MODE;
  51. Constant DEBUG;
  52. Constant Grammar__Version 2;
  53. Include "linklpa";
  54. ENDIF;
  55.  
  56. ! ============================================================================
  57. !   Global variables and their associated Constant and Array declarations
  58. ! ----------------------------------------------------------------------------
  59. Global location = InformLibrary;     ! Must be first global defined
  60. Global sline1;                       ! Must be second
  61. Global sline2;                       ! Must be third
  62.                                      ! (for status line display)
  63. ! ------------------------------------------------------------------------------
  64. !   Z-Machine and interpreter issues
  65. ! ------------------------------------------------------------------------------
  66. Global top_object;                   ! Largest valid number of any tree object
  67. Global standard_interpreter;         ! The version number of the Z-Machine
  68.                                      ! Standard which the interpreter claims
  69.                                      ! to support, in form (upper byte).(lower)
  70. Global undo_flag;                    ! Can the interpreter provide "undo"?
  71. Global just_undone;                  ! Can't have two successive UNDOs
  72. Global transcript_mode;              ! true when game scripting is on
  73. IFDEF DEBUG;
  74. Global xcommsdir;                    ! true if command recording is on
  75. ENDIF;
  76. ! ------------------------------------------------------------------------------
  77. !   Time and score
  78. ! (for linkage reasons, the task_* arrays are created not here but in verblib.h)
  79. ! ------------------------------------------------------------------------------
  80. Global turns = 1;                    ! Number of turns of play so far
  81. Global the_time = NULL;              ! Current time (in minutes since midnight)
  82. Global time_rate = 1;                ! How often time is updated
  83. Global time_step;                    ! By how much
  84.  
  85. Constant MAX_TIMERS  64;             ! Actually only 32 (it's counting in bytes)
  86. Array  the_timers  --> 32;
  87. Global active_timers;                ! Number of timers/daemons actives
  88.  
  89. Global score;                        ! The current score
  90. Global last_score;                   ! Score last turn (for testing for changes)
  91. Global notify_mode = true;           ! Score notification
  92. Global places_score;                 ! Contribution to score made by visiting
  93. Global things_score;                 ! Contribution made by acquisition
  94. ! ------------------------------------------------------------------------------
  95. !   The player
  96. ! ------------------------------------------------------------------------------
  97. Global player;                       ! Which object the human is playing through
  98. Global deadflag;                     ! Normally 0, or false; 1 for dead;
  99.                                      ! 2 for victorious, and higher numbers
  100.                                      ! represent exotic forms of death
  101. ! ------------------------------------------------------------------------------
  102. !   Light and room descriptions
  103. ! ------------------------------------------------------------------------------
  104. Global lightflag = true;             ! Is there currently light to see by?
  105. Global real_location;                ! When in darkness, location = thedark
  106.                                      ! and this holds the real location
  107. Global visibility_ceiling;           ! Highest object in tree visible from
  108.                                      ! the player's point of view (usually
  109.                                      ! the room, sometimes darkness, sometimes
  110.                                      ! a closed non-transparent container).
  111.  
  112. Global lookmode = 1;                 ! 1=standard, 2=verbose, 3=brief room descs
  113. Global print_player_flag;            ! If set, print something like "(as Fred)"
  114.                                      ! in room descriptions, to reveal whom
  115.                                      ! the human is playing through
  116. Global lastdesc;                     ! Value of location at time of most recent
  117.                                      ! room description printed out
  118. ! ------------------------------------------------------------------------------
  119. !   List writing  (style bits are defined as Constants in "verblibm.h")
  120. ! ------------------------------------------------------------------------------
  121. Global c_style;                      ! Current list-writer style
  122. Global lt_value;                     ! Common value of list_together
  123. Global listing_together;             ! Object number of one member of a group
  124.                                      ! being listed together
  125. Global listing_size;                 ! Size of such a group
  126. Global wlf_indent;                   ! Current level of indentation printed by
  127.                                      ! WriteListFrom routine
  128.  
  129. Global inventory_stage = 1;          ! 1 or 2 according to the context in which
  130.                                      ! "invent" routines of objects are called
  131. Global inventory_style;              ! List-writer style currently used while
  132.                                      ! printing inventories
  133. ! ------------------------------------------------------------------------------
  134. !   Menus and printing
  135. ! ------------------------------------------------------------------------------
  136. Global pretty_flag = true;           ! Use character graphics, or plain text?
  137. Global menu_nesting;                 ! Level of nesting (0 = root menu)
  138. Global menu_item;                    ! These are used in communicating
  139. Global item_width = 8;               ! with the menu-creating routines
  140. Global item_name = "---";
  141.  
  142. Global lm_n;                         ! Parameters used by LibraryMessages
  143. Global lm_o;                         ! mechanism
  144.  
  145. IFDEF DEBUG;
  146. Global debug_flag;                   ! Bitmap of flags for tracing actions,
  147.                                      ! calls to object routines, etc.
  148. Global x_scope_count;                ! Used in printing a list of everything
  149.                                      ! in scope
  150. ENDIF;
  151. ! ------------------------------------------------------------------------------
  152. !   Action processing
  153. ! ------------------------------------------------------------------------------
  154. Global action;                       ! Action currently being asked to perform
  155. Global inp1;                         ! 0 (nothing), 1 (number) or first noun
  156. Global inp2;                         ! 0 (nothing), 1 (number) or second noun
  157. Global noun;                         ! First noun or numerical value
  158. Global second;                       ! Second noun or numerical value
  159.  
  160. Global keep_silent;                  ! If true, attempt to perform the action
  161.                                      ! silently (e.g. for implicit takes,
  162.                                      ! implicit opening of unlocked doors)
  163.  
  164. Global reason_code;                  ! Reason for calling a "life" rule
  165.                                      ! (an action or fake such as ##Kiss)
  166.  
  167. Global receive_action;               ! Either ##PutOn or ##Insert, whichever
  168.                                      ! is action being tried when an object's
  169.                                      ! "before" rule is checking "Receive"
  170. ! ==============================================================================
  171. !   Parser variables: first, for communication to the parser
  172. ! ------------------------------------------------------------------------------
  173. Global parser_trace = 0;             ! Set this to 1 to make the parser trace
  174.                                      ! tokens and lines
  175. Global parser_action;                ! For the use of the parser when calling
  176. Global parser_one;                   ! user-supplied routines
  177. Global parser_two;                   !
  178. Array  inputobjs       --> 16;       ! For parser to write its results in
  179. Global parser_inflection;            ! A property (usually "name") to find
  180.                                      ! object names in
  181. ! ------------------------------------------------------------------------------
  182. !   Parser output
  183. ! ------------------------------------------------------------------------------
  184. Global actor;                        ! Person asked to do something
  185. Global actors_location;              ! Like location, but for the actor
  186. Global meta;                         ! Verb is a meta-command (such as "save")
  187.  
  188. Array  multiple_object --> 64;       ! List of multiple parameters
  189. Global multiflag;                    ! Multiple-object flag
  190. Global toomany_flag;                 ! Flag for "multiple match too large"
  191.                                      ! (e.g. if "take all" took over 100 things)
  192.  
  193. Global special_word;                 ! Dictionary address for "special" token
  194. Global special_number;               ! Number typed for "special" token
  195. Global parsed_number;                ! For user-supplied parsing routines
  196. Global consult_from;                 ! Word that a "consult" topic starts on
  197. Global consult_words;                ! ...and number of words in topic
  198. ! ------------------------------------------------------------------------------
  199. !   Implicit taking
  200. ! ------------------------------------------------------------------------------
  201. Global notheld_mode;                 ! To do with implicit taking
  202. Global onotheld_mode;                !     "old copy of notheld_mode", ditto
  203. Global not_holding;                  ! Object to be automatically taken as an
  204.                                      ! implicit command
  205. Array  kept_results --> 16;          ! Delayed command (while the take happens)
  206. ! ------------------------------------------------------------------------------
  207. !   Error numbers when parsing a grammar line
  208. ! ------------------------------------------------------------------------------
  209. Global etype;                        ! Error number on current line
  210. Global best_etype;                   ! Preferred error number so far
  211. Global nextbest_etype;               ! Preferred one, if ASKSCOPE_PE disallowed
  212.  
  213. Constant STUCK_PE     = 1;
  214. Constant UPTO_PE      = 2;
  215. Constant NUMBER_PE    = 3;
  216. Constant CANTSEE_PE   = 4;
  217. Constant TOOLIT_PE    = 5;
  218. Constant NOTHELD_PE   = 6;
  219. Constant MULTI_PE     = 7;
  220. Constant MMULTI_PE    = 8;
  221. Constant VAGUE_PE     = 9;
  222. Constant EXCEPT_PE    = 10;
  223. Constant ANIMA_PE     = 11;
  224. Constant VERB_PE      = 12;
  225. Constant SCENERY_PE   = 13;
  226. Constant ITGONE_PE    = 14;
  227. Constant JUNKAFTER_PE = 15;
  228. Constant TOOFEW_PE    = 16;
  229. Constant NOTHING_PE   = 17;
  230. Constant ASKSCOPE_PE  = 18;
  231. ! ------------------------------------------------------------------------------
  232. !   Pattern-matching against a single grammar line
  233. ! ------------------------------------------------------------------------------
  234. Array pattern --> 32;                ! For the current pattern match
  235. Global pcount;                       ! and a marker within it
  236. Array pattern2 --> 32;               ! And another, which stores the best match
  237. Global pcount2;                      ! so far
  238. Constant PATTERN_NULL = $ffff;       ! Entry for a token producing no text
  239.  
  240. Array  line_ttype-->32;              ! For storing an analysed grammar line
  241. Array  line_tdata-->32;
  242. Array  line_token-->32;
  243.  
  244. Global parameters;                   ! Parameters (objects) entered so far
  245. Global nsns;                         ! Number of special_numbers entered so far
  246. Global special_number1;              ! First number, if one was typed
  247. Global special_number2;              ! Second number, if two were typed
  248. ! ------------------------------------------------------------------------------
  249. !   Inferences and looking ahead
  250. ! ------------------------------------------------------------------------------
  251. Global params_wanted;                ! Number of parameters needed
  252.                                      ! (which may change in parsing)
  253.  
  254. Global inferfrom;                    ! The point from which the rest of the
  255.                                      ! command must be inferred
  256. Global inferword;                    ! And the preposition inferred
  257. Global dont_infer;                   ! Another dull flag
  258.  
  259. Global action_to_be;                 ! (If the current line were accepted.)
  260. Global action_reversed;              ! (Parameters would be reversed in order.)
  261. Global advance_warning;              ! What a later-named thing will be
  262. ! ------------------------------------------------------------------------------
  263. !   At the level of individual tokens now
  264. ! ------------------------------------------------------------------------------
  265. Global found_ttype;                  ! Used to break up tokens into type
  266. Global found_tdata;                  ! and data (by AnalyseToken)
  267. Global token_filter;                 ! For noun filtering by user routines
  268.  
  269. Global length_of_noun;               ! Set by NounDomain to no of words in noun
  270. Constant REPARSE_CODE = 10000;       ! Signals "reparse the text" as a reply
  271.                                      ! from NounDomain
  272.  
  273. Global lookahead;                    ! The token after the one now being matched
  274.  
  275. Global multi_mode;                   ! Multiple mode
  276. Global multi_wanted;                 ! Number of things needed in multitude
  277. Global multi_had;                    ! Number of things actually found
  278. Global multi_context;                ! What token the multi-obj was accepted for
  279.  
  280. Global indef_mode;                   ! "Indefinite" mode - ie, "take a brick"
  281.                                      ! is in this mode
  282. Global indef_type;                   ! Bit-map holding types of specification
  283. Global indef_wanted;                 ! Number of items wanted (100 for all)
  284. Global indef_guess_p;                ! Plural-guessing flag
  285. Global indef_owner;                  ! Object which must hold these items
  286. Global indef_cases;                  ! Possible gender and numbers of them
  287. Global indef_possambig;              ! Has a possibly dangerous assumption
  288.                                      ! been made about meaning of a descriptor?
  289. Global indef_nspec_at;               ! Word at which a number like "two" was
  290.                                      ! parsed (for backtracking)
  291. Global allow_plurals;                ! Whether plurals presently allowed or not
  292.  
  293. Global take_all_rule;                ! Slightly different rules apply to
  294.                                      ! "take all" than other uses of multiple
  295.                                      ! objects, to make adjudication produce
  296.                                      ! more pragmatically useful results
  297.                                      ! (Not a flag: possible values 0, 1, 2)
  298.  
  299. Global dict_flags_of_noun;           ! Of the noun currently being parsed
  300.                                      ! (a bitmap in #dict_par1 format)
  301. Global pronoun_word;                 ! Records which pronoun ("it", "them", ...)
  302.                                      ! caused an error
  303. Global pronoun_obj;                  ! And what obj it was thought to refer to
  304. Global pronoun__word;                ! Saved value
  305. Global pronoun__obj;                 ! Saved value
  306. ! ------------------------------------------------------------------------------
  307. !   Searching through scope and parsing "scope=Routine" grammar tokens
  308. ! ------------------------------------------------------------------------------
  309. Constant PARSING_REASON       = 0;   ! Possible reasons for searching scope
  310. Constant TALKING_REASON       = 1;
  311. Constant EACH_TURN_REASON     = 2;
  312. Constant REACT_BEFORE_REASON  = 3;
  313. Constant REACT_AFTER_REASON   = 4;
  314. Constant LOOPOVERSCOPE_REASON = 5;
  315. Constant TESTSCOPE_REASON     = 6;
  316.  
  317. Global scope_reason = PARSING_REASON; ! Current reason for searching scope
  318.  
  319. Global scope_token;                  ! For "scope=Routine" grammar tokens
  320. Global scope_error;
  321. Global scope_stage;                  ! 1, 2 then 3
  322.  
  323. Global ats_flag = 0;                 ! For AddToScope routines
  324. Global ats_hls;                      !
  325.  
  326. Global placed_in_flag;               ! To do with PlaceInScope
  327.  
  328. ! ------------------------------------------------------------------------------
  329. !   The match list of candidate objects for a given token
  330. ! ------------------------------------------------------------------------------
  331. Constant MATCH_LIST_SIZE = 128;
  332. Array  match_list    --> 64;         ! An array of matched objects so far
  333. Array  match_classes --> 64;         ! An array of equivalence classes for them
  334. Array  match_scores --> 64;          ! An array of match scores for them
  335. Global number_matched;               ! How many items in it?  (0 means none)
  336. Global number_of_classes;            ! How many equivalence classes?
  337. Global match_length;                 ! How many words long are these matches?
  338. Global match_from;                   ! At what word of the input do they begin?
  339. ! ------------------------------------------------------------------------------
  340. !   Low level textual manipulation
  341. ! ------------------------------------------------------------------------------
  342. Array  buffer    string 120;         ! Buffer for parsing main line of input
  343. Array  parse     string 64;          ! Parse table mirroring it
  344. Array  buffer2   string 120;         ! Buffers for supplementary questions
  345. Array  parse2    string 64;          !
  346. Array  buffer3   string 120;         ! Buffer retaining input for "again"
  347.  
  348. Constant comma_word = 'comma,';      ! An "untypeable word" used to substitute
  349.                                      ! for commas in parse buffers
  350.  
  351. Global wn;                           ! Word number within "parse" (from 1)
  352. Global num_words;                    ! Number of words typed
  353. Global verb_word;                    ! Verb word (eg, take in "take all" or
  354.                                      ! "dwarf, take all") - address in dict
  355. Global verb_wordnum;                 ! its number in typing order (eg, 1 or 3)
  356. Global usual_grammar_after;          ! Point from which usual grammar is parsed
  357.                                      ! (it may vary from the above if user's
  358.                                      ! routines match multi-word verbs)
  359.  
  360. Global oops_from;                    ! The "first mistake" word number
  361. Global saved_oops;                   ! Used in working this out
  362. Array  oops_workspace -> 64;         ! Used temporarily by "oops" routine
  363.  
  364. Global held_back_mode;               ! Flag: is there some input from last time
  365. Global hb_wn;                        ! left over?  (And a save value for wn.)
  366.                                      ! (Used for full stops and "then".)
  367. ! ----------------------------------------------------------------------------
  368. Array PowersOfTwo_TB                 ! Used in converting case numbers to
  369.   --> $$100000000000                 ! case bitmaps
  370.       $$010000000000
  371.       $$001000000000
  372.       $$000100000000
  373.       $$000010000000
  374.       $$000001000000
  375.       $$000000100000
  376.       $$000000010000
  377.       $$000000001000
  378.       $$000000000100
  379.       $$000000000010
  380.       $$000000000001;
  381. ! ============================================================================
  382.  
  383.  
  384. ! ============================================================================
  385. !  Constants, and one variable, needed for the language definition file
  386. ! ----------------------------------------------------------------------------
  387. Constant POSSESS_PK  = $100;
  388. Constant DEFART_PK   = $101;
  389. Constant INDEFART_PK = $102;
  390. Global short_name_case;
  391. ! ----------------------------------------------------------------------------
  392. Include "language__";                !  The natural language definition,
  393.                                      !  whose filename is taken from the ICL
  394.                                      !  language_name variable
  395. ! ----------------------------------------------------------------------------
  396. #ifndef LanguageCases;
  397. Constant LanguageCases = 1;
  398. #endif;
  399. ! ------------------------------------------------------------------------------
  400. !   Pronouns support for the cruder (library 6/2 and earlier) version:
  401. !   only needed in English
  402. ! ------------------------------------------------------------------------------
  403. #ifdef EnglishNaturalLanguage;
  404. Global itobj = NULL;                 ! The object which is currently "it"
  405. Global himobj = NULL;                ! The object which is currently "him"
  406. Global herobj = NULL;                ! The object which is currently "her"
  407.  
  408. Global old_itobj = NULL;             ! The object which is currently "it"
  409. Global old_himobj = NULL;            ! The object which is currently "him"
  410. Global old_herobj = NULL;            ! The object which is currently "her"
  411. #endif;
  412. ! ============================================================================
  413.  
  414.  
  415. ! ============================================================================
  416. ! "Darkness" is not really a place: but it has to be an object so that the
  417. !  location-name on the status line can be "Darkness".
  418. ! ----------------------------------------------------------------------------
  419. Object thedark "(darkness object)"
  420.   with initial 0,
  421.        short_name DARKNESS__TX,
  422.        description
  423.        [;  return L__M(##Miscellany, 17);
  424.        ];
  425. Object selfobj "(self object)"
  426.   with short_name
  427.        [;  return L__M(##Miscellany, 18);
  428.        ],
  429.        description
  430.        [;  return L__M(##Miscellany, 19);
  431.        ],
  432.        before NULL,   after NULL,    life NULL,    each_turn NULL,
  433.        time_out NULL, describe NULL,
  434.        capacity 100, parse_name 0,
  435.        orders 0, number 0,
  436.   has  concealed animate proper transparent;
  437.  
  438. ! ============================================================================
  439. !  The definition of the token-numbering system used by Inform.
  440. ! ----------------------------------------------------------------------------
  441.  
  442. Constant ILLEGAL_TT        = 0;      ! Types of grammar token: illegal
  443. Constant ELEMENTARY_TT     = 1;      !     (one of those below)
  444. Constant PREPOSITION_TT    = 2;      !     e.g. 'into'
  445. Constant ROUTINE_FILTER_TT = 3;      !     e.g. noun=CagedCreature
  446. Constant ATTR_FILTER_TT    = 4;      !     e.g. edible
  447. Constant SCOPE_TT          = 5;      !     e.g. scope=Spells
  448. Constant GPR_TT            = 6;      !     a general parsing routine
  449.  
  450. Constant NOUN_TOKEN        = 0;      ! The elementary grammar tokens, and
  451. Constant HELD_TOKEN        = 1;      ! the numbers compiled by Inform to
  452. Constant MULTI_TOKEN       = 2;      ! encode them
  453. Constant MULTIHELD_TOKEN   = 3;
  454. Constant MULTIEXCEPT_TOKEN = 4;
  455. Constant MULTIINSIDE_TOKEN = 5;
  456. Constant CREATURE_TOKEN    = 6;
  457. Constant SPECIAL_TOKEN     = 7;
  458. Constant NUMBER_TOKEN      = 8;
  459. Constant TOPIC_TOKEN       = 9;
  460.  
  461. Constant GPR_FAIL          = -1;     ! Return values from General Parsing
  462. Constant GPR_PREPOSITION   = 0;      ! Routines
  463. Constant GPR_NUMBER        = 1;
  464. Constant GPR_REPARSE       = REPARSE_CODE;
  465. Constant GPR_NOUN          = $ff00;
  466. Constant GPR_HELD          = $ff01;
  467. Constant GPR_MULTI         = $ff02;
  468. Constant GPR_MULTIHELD     = $ff03;
  469. Constant GPR_MULTIEXCEPT   = $ff04;
  470. Constant GPR_MULTIINSIDE   = $ff05;
  471. Constant GPR_CREATURE      = $ff06;
  472.  
  473. Constant ENDIT_TOKEN       = 15;     ! Value used to mean "end of grammar line"
  474.  
  475. #Iftrue Grammar__Version == 1;
  476. [ AnalyseToken token m;
  477.  
  478.     found_tdata = token;
  479.  
  480.     if (token < 0)   { found_ttype = ILLEGAL_TT; return; }
  481.     if (token <= 8)  { found_ttype = ELEMENTARY_TT; return; }
  482.     if (token < 15)  { found_ttype = ILLEGAL_TT; return; }
  483.     if (token == 15) { found_ttype = ELEMENTARY_TT; return; }
  484.     if (token < 48)  { found_ttype = ROUTINE_FILTER_TT;
  485.                        found_tdata = token - 16;
  486.                        return; }
  487.     if (token < 80)  { found_ttype = GPR_TT;
  488.                        found_tdata = #preactions_table-->(token-48);
  489.                        return; }
  490.     if (token < 128) { found_ttype = SCOPE_TT;
  491.                        found_tdata = #preactions_table-->(token-80);
  492.                        return; }
  493.     if (token < 180) { found_ttype = ATTR_FILTER_TT;
  494.                        found_tdata = token - 128;
  495.                        return; }
  496.  
  497.     found_ttype = PREPOSITION_TT;
  498.     m=#adjectives_table;
  499.     for (::)
  500.     {   if (token==m-->1) { found_tdata = m-->0; return; }
  501.         m=m+4;
  502.     }
  503.     m=#adjectives_table; RunTimeError(1);
  504.     found_tdata = m;
  505. ];
  506. [ UnpackGrammarLine line_address i m;
  507.   for (i = 0 : i < 32 : i++)
  508.   {   line_token-->i = ENDIT_TOKEN;
  509.       line_ttype-->i = ELEMENTARY_TT;
  510.       line_tdata-->i = ENDIT_TOKEN;
  511.   }
  512.   for (i = 0: i <= 5 :i++)
  513.   {   line_token-->i = line_address->(i+1);
  514.       AnalyseToken(line_token-->i);
  515.       if ((found_ttype == ELEMENTARY_TT) && (found_tdata == NOUN_TOKEN)
  516.           && (m == line_address->0))
  517.       {   line_token-->i = ENDIT_TOKEN;
  518.           break;
  519.       }
  520.       line_ttype-->i = found_ttype;
  521.       line_tdata-->i = found_tdata;
  522.       if (found_ttype ~= PREPOSITION_TT) m++;
  523.   }
  524.   action_to_be = line_address->7;
  525.   action_reversed = false;
  526.   params_wanted = line_address->0;
  527.   return line_address + 8;
  528. ];
  529. #Ifnot;
  530. [ AnalyseToken token;
  531.  
  532.     if (token == ENDIT_TOKEN)
  533.     {   found_ttype = ELEMENTARY_TT;
  534.         found_tdata = ENDIT_TOKEN;
  535.         return;
  536.     }
  537.  
  538.     found_ttype = (token->0) & $$1111;
  539.     found_tdata = (token+1)-->0;
  540. ];
  541. [ UnpackGrammarLine line_address i;
  542.   for (i = 0 : i < 32 : i++)
  543.   {   line_token-->i = ENDIT_TOKEN;
  544.       line_ttype-->i = ELEMENTARY_TT;
  545.       line_tdata-->i = ENDIT_TOKEN;
  546.   }
  547.   action_to_be = 256*(line_address->0) + line_address->1;
  548.   action_reversed = ((action_to_be & $400) ~= 0);
  549.   action_to_be = action_to_be & $3ff;
  550.   line_address--;
  551.   params_wanted = 0;
  552.   for (i=0::i++)
  553.   {   line_address = line_address + 3;
  554.       if (line_address->0 == ENDIT_TOKEN) break;
  555.       line_token-->i = line_address;
  556.       AnalyseToken(line_address);
  557.       if (found_ttype ~= PREPOSITION_TT) params_wanted++;
  558.       line_ttype-->i = found_ttype;
  559.       line_tdata-->i = found_tdata;
  560.   }
  561.   return line_address + 1;
  562. ];
  563. #Endif;
  564.  
  565. !  To protect against a bug in early versions of the "Zip" interpreter:
  566.  
  567. [ Tokenise__ b p; b->(2 + b->1) = 0; @tokenise b p; ];
  568.  
  569. ! ============================================================================
  570. !  The InformParser object abstracts the front end of the parser.
  571. !
  572. !  InformParser.parse_input(results)
  573. !  returns only when a sensible request has been made, and puts into the
  574. !  "results" buffer:
  575. !
  576. !  --> 0 = The action number
  577. !  --> 1 = Number of parameters
  578. !  --> 2, 3, ... = The parameters (object numbers), but
  579. !                  0 means "put the multiple object list here"
  580. !                  1 means "put one of the special numbers here"
  581. !
  582. ! ----------------------------------------------------------------------------
  583.  
  584. Object InformParser "(Inform Parser)"
  585.   with parse_input
  586.        [ results; Parser__parse(results);
  587.        ], has proper;
  588.  
  589. ! ----------------------------------------------------------------------------
  590. !  The Keyboard routine actually receives the player's words,
  591. !  putting the words in "a_buffer" and their dictionary addresses in
  592. !  "a_table".  It is assumed that the table is the same one on each
  593. !  (standard) call.
  594. !
  595. !  It can also be used by miscellaneous routines in the game to ask
  596. !  yes-no questions and the like, without invoking the rest of the parser.
  597. !
  598. !  Return the number of words typed
  599. ! ----------------------------------------------------------------------------
  600.  
  601. [ Keyboard  a_buffer a_table  nw i w w2 x1 x2;
  602.  
  603.     DisplayStatus();
  604.     .FreshInput;
  605.  
  606. !  Save the start of the buffer, in case "oops" needs to restore it
  607. !  to the previous time's buffer
  608.  
  609.     for (i=0:i<64:i++) oops_workspace->i = a_buffer->i;
  610.  
  611. !  In case of an array entry corruption that shouldn't happen, but would be
  612. !  disastrous if it did:
  613.  
  614.    a_buffer->0 = 120;
  615.    a_table->0 = 64;
  616.  
  617. !  Print the prompt, and read in the words and dictionary addresses
  618.  
  619.     L__M(##Prompt);
  620.     AfterPrompt();
  621.     #IFV3; read a_buffer a_table; #ENDIF;
  622.     temp_global = 0;
  623.     #IFV5; read a_buffer a_table DrawStatusLine; #ENDIF;
  624.     nw=a_table->1;
  625.  
  626. !  If the line was blank, get a fresh line
  627.     if (nw == 0)
  628.     { L__M(##Miscellany,10); jump FreshInput; }
  629.  
  630. !  Unless the opening word was "oops", return
  631.  
  632.     w=a_table-->1;
  633.     if (w == OOPS1__WD or OOPS2__WD or OOPS3__WD) jump DoOops;
  634.  
  635. #IFV5;
  636. !  Undo handling
  637.  
  638.     if ((w == UNDO1__WD or UNDO2__WD or UNDO3__WD) && (parse->1==1))
  639.     {   if (turns==1)
  640.         {   L__M(##Miscellany,11); jump FreshInput;
  641.         }
  642.         if (undo_flag==0)
  643.         {   L__M(##Miscellany,6); jump FreshInput;
  644.         }
  645.         if (undo_flag==1) jump UndoFailed;
  646.         if (just_undone==1)
  647.         {   L__M(##Miscellany,12); jump FreshInput;
  648.         }
  649.         @restore_undo i;
  650.         if (i==0)
  651.         {   .UndoFailed;
  652.             L__M(##Miscellany,7);
  653.         }
  654.         jump FreshInput;
  655.     }
  656.     @save_undo i;
  657.     just_undone=0;
  658.     undo_flag=2;
  659.     if (i==-1) undo_flag=0;
  660.     if (i==0) undo_flag=1;
  661.     if (i==2)
  662.     {   style bold;
  663.         print (name) location, "^";
  664.         style roman;
  665.         L__M(##Miscellany,13);
  666.         just_undone=1;
  667.         jump FreshInput;
  668.     }
  669. #ENDIF;
  670.  
  671.     return nw;
  672.  
  673.     .DoOops;
  674.     if (oops_from == 0)
  675.     {   L__M(##Miscellany,14); jump FreshInput; }
  676.     if (nw == 1)
  677.     {   L__M(##Miscellany,15); jump FreshInput; }
  678.     if (nw > 2)
  679.     {   L__M(##Miscellany,16); jump FreshInput; }
  680.  
  681. !  So now we know: there was a previous mistake, and the player has
  682. !  attempted to correct a single word of it.
  683.  
  684.     for (i=0:i<=120:i++) buffer2->i = a_buffer->i;
  685.     x1 = a_table->9; ! Start of word following "oops"
  686.     x2 = a_table->8; ! Length of word following "oops"
  687.  
  688. !  Repair the buffer to the text that was in it before the "oops"
  689. !  was typed:
  690.  
  691.     for (i=0:i<64:i++) a_buffer->i = oops_workspace->i;
  692.     Tokenise__(a_buffer,a_table);
  693.  
  694. !  Work out the position in the buffer of the word to be corrected:
  695.  
  696.     w = a_table->(4*oops_from + 1); ! Start of word to go
  697.     w2 = a_table->(4*oops_from);    ! Length of word to go
  698.  
  699. !  Write spaces over the word to be corrected:
  700.  
  701.     for (i=0:i<w2:i++) a_buffer->(i+w) = ' ';
  702.  
  703.     if (w2 < x2)
  704.     {   ! If the replacement is longer than the original, move up...
  705.  
  706.         for (i=120:i>=w+x2:i--)
  707.             a_buffer->i = a_buffer->(i-x2+w2);
  708.  
  709.         ! ...increasing buffer size accordingly.
  710.  
  711.         a_buffer->1 = (a_buffer->1) + (x2-w2);
  712.     }
  713.  
  714. !  Write the correction in:
  715.  
  716.     for (i=0:i<x2:i++) a_buffer->(i+w) = buffer2->(i+x1);
  717.  
  718.     Tokenise__(a_buffer,a_table);
  719.     nw=a_table->1;
  720.  
  721.     return nw;
  722. ];
  723.  
  724. ! ----------------------------------------------------------------------------
  725. !  To simplify the picture a little, a rough map of the main routine:
  726. !
  727. !  (A)    Get the input, do "oops" and "again"
  728. !  (B)    Is it a direction, and so an implicit "go"?  If so go to (K)
  729. !  (C)    Is anyone being addressed?
  730. !  (D)    Get the verb: try all the syntax lines for that verb
  731. !  (E)    Break down a syntax line into analysed tokens
  732. !  (F)    Look ahead for advance warning for multiexcept/multiinside
  733. !  (G)    Parse each token in turn (calling ParseToken to do most of the work)
  734. !  (H)    Cheaply parse otherwise unrecognised conversation and return
  735. !  (I)    Print best possible error message
  736. !  (J)    Retry the whole lot
  737. !  (K)    Last thing: check for "then" and further instructions(s), return.
  738. !
  739. !  The strategic points (A) to (K) are marked in the commentary.
  740. !
  741. !  Note that there are three different places where a return can happen.
  742. ! ----------------------------------------------------------------------------
  743.  
  744. [ Parser__parse  results   syntax line num_lines line_address i j k
  745.                            token l m;
  746.  
  747. !  **** (A) ****
  748.  
  749. !  Firstly, in "not held" mode, we still have a command left over from last
  750. !  time (eg, the user typed "eat biscuit", which was parsed as "take biscuit"
  751. !  last time, with "eat biscuit" tucked away until now).  So we return that.
  752.  
  753.     if (notheld_mode==1)
  754.     {   for (i=0:i<8:i++) results-->i=kept_results-->i;
  755.         notheld_mode=0; rtrue;
  756.     }
  757.  
  758.     if (held_back_mode==1)
  759.     {   held_back_mode=0;
  760.         Tokenise__(buffer,parse);
  761.         jump ReParse;
  762.     }
  763.  
  764.   .ReType;
  765.  
  766.     Keyboard(buffer,parse);
  767.  
  768.   .ReParse;
  769.  
  770.     parser_inflection = name;
  771.  
  772. !  Initially assume the command is aimed at the player, and the verb
  773. !  is the first word
  774.  
  775.     num_words=parse->1;
  776.     wn=1;
  777. #ifdef LanguageToInformese;
  778.     LanguageToInformese();
  779. #ifv5;
  780. !   Re-tokenise:
  781.     Tokenise__(buffer,parse);
  782. #endif;
  783. #endif;
  784.  
  785.     BeforeParsing();
  786.     num_words=parse->1;
  787.  
  788.     k=0;
  789. #ifdef DEBUG;
  790.     if (parser_trace>=2)
  791.     {   print "[ ";
  792.         for (i=0:i<num_words:i++)
  793.         {   j=parse-->(i*2 + 1);
  794.             k=WordAddress(i+1);
  795.             l=WordLength(i+1);
  796.             print "~"; for (m=0:m<l:m++) print (char) k->m; print "~ ";
  797.  
  798.             if (j == 0) print "?";
  799.             else
  800.             {   if (UnsignedCompare(j, 0-->4)>=0
  801.                     && UnsignedCompare(j, 0-->2)<0) print (address) j;
  802.                 else print j;
  803.             }
  804.             if (i ~= num_words-1) print " / ";
  805.         }
  806.         print " ]^";
  807.     }
  808. #endif;
  809.     verb_wordnum=1;
  810.     actor=player;
  811.     actors_location = ScopeCeiling(player);
  812.     usual_grammar_after = 0;
  813.  
  814.   .AlmostReParse;
  815.  
  816.     scope_token = 0;
  817.     action_to_be = NULL;
  818.  
  819. !  Begin from what we currently think is the verb word
  820.  
  821.   .BeginCommand;
  822.     wn=verb_wordnum;
  823.     verb_word = NextWordStopped();
  824.  
  825. !  If there's no input here, we must have something like
  826. !  "person,".
  827.  
  828.     if (verb_word==-1)
  829.     {   best_etype = STUCK_PE; jump GiveError; }
  830.  
  831. !  Now try for "again" or "g", which are special cases:
  832. !  don't allow "again" if nothing has previously been typed;
  833. !  simply copy the previous text across
  834.  
  835.     if (verb_word==AGAIN2__WD or AGAIN3__WD) verb_word=AGAIN1__WD;
  836.     if (verb_word==AGAIN1__WD)
  837.     {   if (actor~=player)
  838.         {   L__M(##Miscellany,20); jump ReType; }
  839.         if (buffer3->1==0)
  840.         {   L__M(##Miscellany,21); jump ReType; }
  841.         for (i=0:i<120:i++) buffer->i=buffer3->i;
  842.         jump ReParse;
  843.     }
  844.  
  845. !  Save the present input in case of an "again" next time
  846.  
  847.     if (verb_word~=AGAIN1__WD)
  848.         for (i=0:i<120:i++) buffer3->i=buffer->i;
  849.  
  850.     if (usual_grammar_after==0)
  851.     {   i = RunRoutines(actor, grammar);
  852.         #ifdef DEBUG;
  853.         if (parser_trace>=2 && actor.grammar~=0 or NULL)
  854.             print " [Grammar property returned ", i, "]^";
  855.         #endif;
  856.         if (i<0) { usual_grammar_after = verb_wordnum; i=-i; }
  857.         if (i==1)
  858.         {   results-->0 = action;
  859.             results-->1 = noun;
  860.             results-->2 = second;
  861.             rtrue;
  862.         }
  863.         if (i~=0) { verb_word = i; wn--; verb_wordnum--; }
  864.         else
  865.         {   wn = verb_wordnum; verb_word=NextWord();
  866.         }
  867.     }
  868.     else usual_grammar_after=0;
  869.  
  870. !  **** (B) ****
  871.  
  872.     #ifdef LanguageIsVerb;
  873.     if (verb_word==0)
  874.     {   i = wn; verb_word=LanguageIsVerb(buffer, parse, verb_wordnum);
  875.         wn = i;
  876.     }
  877.     #endif;
  878.  
  879. !  If the first word is not listed as a verb, it must be a direction
  880. !  or the name of someone to talk to
  881.  
  882.     if (verb_word==0 || ((verb_word->#dict_par1) & 1) == 0)
  883.     {   
  884.  
  885. !  So is the first word an object contained in the special object "compass"
  886. !  (i.e., a direction)?  This needs use of NounDomain, a routine which
  887. !  does the object matching, returning the object number, or 0 if none found,
  888. !  or REPARSE_CODE if it has restructured the parse table so the whole parse
  889. !  must be begun again...
  890.  
  891.         wn=verb_wordnum; indef_mode = false; token_filter = 0;
  892.         l=NounDomain(compass,0,0); if (l==REPARSE_CODE) jump ReParse;
  893.  
  894. !  If it is a direction, send back the results:
  895. !  action=GoSub, no of arguments=1, argument 1=the direction.
  896.  
  897.         if (l~=0)
  898.         {   results-->0 = ##Go;
  899.             results-->1 = 1;
  900.             results-->2 = l;
  901.             jump LookForMore;
  902.         }
  903.  
  904. !  **** (C) ****
  905.  
  906. !  Only check for a comma (a "someone, do something" command) if we are
  907. !  not already in the middle of one.  (This simplification stops us from
  908. !  worrying about "robot, wizard, you are an idiot", telling the robot to
  909. !  tell the wizard that she is an idiot.)
  910.  
  911.         if (actor==player)
  912.         {   for (j=2:j<=num_words:j++)
  913.             {   i=NextWord(); if (i==comma_word) jump Conversation;
  914.             }
  915.  
  916.             verb_word=UnknownVerb(verb_word);
  917.             if (verb_word~=0) jump VerbAccepted;
  918.         }
  919.  
  920.         best_etype=VERB_PE; jump GiveError;
  921.  
  922. !  NextWord nudges the word number wn on by one each time, so we've now
  923. !  advanced past a comma.  (A comma is a word all on its own in the table.)
  924.  
  925.       .Conversation;
  926.         j=wn-1;
  927.         if (j==1) { L__M(##Miscellany,22); jump ReType; }
  928.  
  929. !  Use NounDomain (in the context of "animate creature") to see if the
  930. !  words make sense as the name of someone held or nearby
  931.  
  932.         wn=1; lookahead=HELD_TOKEN;
  933.         scope_reason = TALKING_REASON;
  934.         l=NounDomain(player,actors_location,6);
  935.         scope_reason = PARSING_REASON;
  936.         if (l==REPARSE_CODE) jump ReParse;
  937.  
  938.         if (l==0) { L__M(##Miscellany,23); jump ReType; }
  939.  
  940. !  The object addressed must at least be "talkable" if not actually "animate"
  941. !  (the distinction allows, for instance, a microphone to be spoken to,
  942. !  without the parser thinking that the microphone is human).
  943.  
  944.         if (l hasnt animate && l hasnt talkable)
  945.         {   L__M(##Miscellany, 24, l); jump ReType; }
  946.  
  947. !  Check that there aren't any mystery words between the end of the person's
  948. !  name and the comma (eg, throw out "dwarf sdfgsdgs, go north").
  949.  
  950.         if (wn~=j)
  951.         {   L__M(##Miscellany, 25); jump ReType; }
  952.  
  953. !  The player has now successfully named someone.  Adjust "him", "her", "it":
  954.  
  955.         PronounNotice(l);
  956.  
  957. !  Set the global variable "actor", adjust the number of the first word,
  958. !  and begin parsing again from there.
  959.  
  960.         verb_wordnum=j+1; actor=l;
  961.         actors_location=ScopeCeiling(l);
  962.         #ifdef DEBUG;
  963.         if (parser_trace>=1)
  964.             print "[Actor is ", (the) actor, " in ",
  965.                 (name) actors_location, "]^";
  966.         #endif;
  967.         jump BeginCommand;
  968.     }
  969.  
  970. !  **** (D) ****
  971.  
  972.    .VerbAccepted;
  973.  
  974. !  We now definitely have a verb, not a direction, whether we got here by the
  975. !  "take ..." or "person, take ..." method.  Get the meta flag for this verb:
  976.  
  977.     meta=((verb_word->#dict_par1) & 2)/2;
  978.  
  979. !  You can't order other people to "full score" for you, and so on...
  980.  
  981.     if (meta==1 && actor~=player)
  982.     {   best_etype=VERB_PE; meta=0; jump GiveError; }
  983.  
  984. !  Now let i be the corresponding verb number, stored in the dictionary entry
  985. !  (in a peculiar 255-n fashion for traditional Infocom reasons)...
  986.  
  987.     i=$ff-(verb_word->#dict_par2);
  988.  
  989. !  ...then look up the i-th entry in the verb table, whose address is at word
  990. !  7 in the Z-machine (in the header), so as to get the address of the syntax
  991. !  table for the given verb...
  992.  
  993.     syntax=(0-->7)-->i;
  994.  
  995. !  ...and then see how many lines (ie, different patterns corresponding to the
  996. !  same verb) are stored in the parse table...
  997.  
  998.     num_lines=(syntax->0)-1;
  999.  
  1000. !  ...and now go through them all, one by one.
  1001. !  To prevent pronoun_word 0 being misunderstood,
  1002.  
  1003.    pronoun_word=NULL; pronoun_obj=NULL;
  1004.  
  1005.    #ifdef DEBUG;
  1006.    if (parser_trace>=1)
  1007.    {    print "[Parsing for the verb '", (address) verb_word,
  1008.               "' (", num_lines+1, " lines)]^";
  1009.    }
  1010.    #endif;
  1011.  
  1012.    best_etype=STUCK_PE; nextbest_etype=STUCK_PE;
  1013.  
  1014. !  "best_etype" is the current failure-to-match error - it is by default
  1015. !  the least informative one, "don't understand that sentence".
  1016. !  "nextbest_etype" remembers the best alternative to having to ask a
  1017. !  scope token for an error message (i.e., the best not counting ASKSCOPE_PE).
  1018.  
  1019.  
  1020. !  **** (E) ****
  1021.  
  1022.     line_address = syntax + 1;
  1023.  
  1024.     for (line=0:line<=num_lines:line++)
  1025.     {   
  1026.         for (i = 0 : i < 32 : i++)
  1027.         {   line_token-->i = ENDIT_TOKEN;
  1028.             line_ttype-->i = ELEMENTARY_TT;
  1029.             line_tdata-->i = ENDIT_TOKEN;
  1030.         }
  1031.  
  1032. !  Unpack the syntax line from Inform format into three arrays; ensure that
  1033. !  the sequence of tokens ends in an ENDIT_TOKEN.
  1034.  
  1035.         line_address = UnpackGrammarLine(line_address);
  1036.             
  1037.         #ifdef DEBUG;
  1038.         if (parser_trace >= 1)
  1039.         {   if (parser_trace >= 2) new_line;
  1040.             print "[line ", line; DebugGrammarLine();
  1041.             print "]^";
  1042.         }
  1043.         #endif;
  1044.  
  1045. !  We aren't in "not holding" or inferring modes, and haven't entered
  1046. !  any parameters on the line yet, or any special numbers; the multiple
  1047. !  object is still empty.
  1048.  
  1049.         not_holding=0;
  1050.         inferfrom=0;
  1051.         parameters=0;
  1052.         nsns=0; special_word=0; special_number=0;
  1053.         multiple_object-->0 = 0;
  1054.         multi_context = 0;
  1055.         etype=STUCK_PE;
  1056.  
  1057. !  Put the word marker back to just after the verb
  1058.  
  1059.         wn=verb_wordnum+1;
  1060.  
  1061. !  **** (F) ****
  1062. !  There are two special cases where parsing a token now has to be
  1063. !  affected by the result of parsing another token later, and these
  1064. !  two cases (multiexcept and multiinside tokens) are helped by a quick
  1065. !  look ahead, to work out the future token now.  We can only carry this
  1066. !  out in the simple (but by far the most common) case:
  1067. !
  1068. !      multiexcept <one or more prepositions> noun
  1069. !
  1070. !  and similarly for multiinside.
  1071.  
  1072.         advance_warning = NULL; indef_mode = false;
  1073.         for (i=0,m=false,pcount=0:line_token-->pcount ~= ENDIT_TOKEN:pcount++)
  1074.         {   scope_token = 0;
  1075.  
  1076.             if (line_ttype-->pcount ~= PREPOSITION_TT) i++;
  1077.  
  1078.             if (line_ttype-->pcount == ELEMENTARY_TT)
  1079.             {   if (line_tdata-->pcount == MULTI_TOKEN) m=true;
  1080.                 if (line_tdata-->pcount
  1081.                     == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN  && i==1)
  1082.                 {   !   First non-preposition is "multiexcept" or
  1083.                     !   "multiinside", so look ahead.
  1084.  
  1085.                     #ifdef DEBUG;
  1086.                     if (parser_trace>=2) print " [Trying look-ahead]^";
  1087.                     #endif;
  1088.  
  1089.                     !   We need this to be followed by 1 or more prepositions.
  1090.  
  1091.                     pcount++;
  1092.                     if (line_ttype-->pcount == PREPOSITION_TT)
  1093.                     {   while (line_ttype-->pcount == PREPOSITION_TT)
  1094.                             pcount++;
  1095.  
  1096.                         if ((line_ttype-->pcount == ELEMENTARY_TT)
  1097.                             && (line_tdata-->pcount == NOUN_TOKEN))
  1098.                         {
  1099.                             !  Advance past the last preposition
  1100.  
  1101.                             while (wn <= num_words)
  1102.                             {   if (NextWord() == line_tdata-->(pcount-1))
  1103.                                 {   l = NounDomain(actors_location, actor,
  1104.                                             NOUN_TOKEN);
  1105.                                     #ifdef DEBUG;
  1106.                                     if (parser_trace>=2)
  1107.                                     {   print " [Advanced to ~noun~ token: ";
  1108.                                         if (l==REPARSE_CODE)
  1109.                                             print "re-parse request]^";
  1110.                                         if (l==1) print "but multiple found]^";
  1111.                                         if (l==0) print "error ", etype, "]^";
  1112.                                         if (l>=2) print (the) l, "]^";
  1113.                                     }
  1114.                                     #endif;
  1115.                                     if (l==REPARSE_CODE) jump ReParse;
  1116.                                     if (l>=2) advance_warning = l;
  1117.                                 }
  1118.                             }
  1119.                         }
  1120.                     }
  1121.                     break;
  1122.                 }
  1123.             }
  1124.         }
  1125.  
  1126. !  Slightly different line-parsing rules will apply to "take multi", to
  1127. !  prevent "take all" behaving correctly but misleadingly when there's
  1128. !  nothing to take.
  1129.  
  1130.         take_all_rule = 0;
  1131.         if (m && params_wanted==1 && action_to_be==##Take)
  1132.             take_all_rule = 1;
  1133.  
  1134. !  And now start again, properly, forearmed or not as the case may be.
  1135. !  As a precaution, we clear all the variables again (they may have been
  1136. !  disturbed by the call to NounDomain, which may have called outside
  1137. !  code, which may have done anything!).
  1138.  
  1139.         not_holding=0;
  1140.         inferfrom=0;
  1141.         parameters=0;
  1142.         nsns=0; special_word=0; special_number=0;
  1143.         multiple_object-->0 = 0;
  1144.         etype=STUCK_PE;
  1145.         wn=verb_wordnum+1;
  1146.  
  1147. !  **** (G) ****
  1148. !  "Pattern" gradually accumulates what has been recognised so far,
  1149. !  so that it may be reprinted by the parser later on
  1150.  
  1151.         for (pcount=1::pcount++)
  1152.         {   pattern-->pcount = PATTERN_NULL; scope_token=0;
  1153.  
  1154.             token = line_token-->(pcount-1);
  1155.             lookahead = line_token-->pcount;
  1156.  
  1157.             #ifdef DEBUG;
  1158.             if (parser_trace >= 2)
  1159.                print " [line ", line, " token ", pcount, " word ", wn, " : ",
  1160.                      (DebugToken) token, "]^";
  1161.             #endif;
  1162.  
  1163.             if (token ~= ENDIT_TOKEN)
  1164.             {   scope_reason = PARSING_REASON;
  1165.                 l = ParseToken(results, pcount-1);
  1166.                 scope_reason = PARSING_REASON;
  1167.  
  1168.                 #ifdef DEBUG;
  1169.                 if (parser_trace >= 3)
  1170.                 {   print "  [token resulted in ";
  1171.                     if (l==REPARSE_CODE) print "re-parse request]^";
  1172.                     if (l==0) print "failure with error type ", etype, "]^";
  1173.                     if (l==1) print "success]^";
  1174.                 }
  1175.                 #endif;
  1176.  
  1177.                 if (l==REPARSE_CODE) jump ReParse;
  1178.                 if (l==0)    break;
  1179.             }
  1180.             else
  1181.             {
  1182.  
  1183. !  If the player has entered enough already but there's still
  1184. !  text to wade through: store the pattern away so as to be able to produce
  1185. !  a decent error message if this turns out to be the best we ever manage,
  1186. !  and in the mean time give up on this line
  1187.  
  1188. !  However, if the superfluous text begins with a comma or "then" then
  1189. !  take that to be the start of another instruction
  1190.  
  1191.                 if (wn <= num_words)
  1192.                 {   l=NextWord();
  1193.                     if (l==THEN1__WD or THEN2__WD or THEN3__WD or comma_word)
  1194.                     {   held_back_mode=1; hb_wn=wn-1; }
  1195.                     else
  1196.                     {   for (m=0:m<32:m++) pattern2-->m=pattern-->m;
  1197.                         pcount2=pcount;
  1198.                         etype=UPTO_PE; break;
  1199.                     }
  1200.                 }
  1201.  
  1202. !  Now, we may need to revise the multiple object because of the single one
  1203. !  we now know (but didn't when the list was drawn up).
  1204.  
  1205.                 if (parameters>=1 && results-->2 == 0)
  1206.                 {   l=ReviseMulti(results-->3);
  1207.                     if (l~=0) { etype=l; break; }
  1208.                 }
  1209.                 if (parameters>=2 && results-->3 == 0)
  1210.                 {   l=ReviseMulti(results-->2);
  1211.                     if (l~=0) { etype=l; break; }
  1212.                 }
  1213.  
  1214. !  To trap the case of "take all" inferring only "yourself" when absolutely
  1215. !  nothing else is in the vicinity...
  1216.  
  1217.                 if (take_all_rule==2 && results-->2 == actor)
  1218.                 {   best_etype = NOTHING_PE; jump GiveError;
  1219.                 }
  1220.  
  1221.                 #ifdef DEBUG;
  1222.                 if (parser_trace>=1)
  1223.                     print "[Line successfully parsed]^";
  1224.                 #endif;
  1225.  
  1226. !  The line has successfully matched the text.  Declare the input error-free...
  1227.  
  1228.                 oops_from = 0;
  1229.  
  1230. !  ...explain any inferences made (using the pattern)...
  1231.  
  1232.                 if (inferfrom~=0)
  1233.                 {   print "("; PrintCommand(inferfrom); print ")^";
  1234.                 }
  1235.  
  1236. !  ...copy the action number, and the number of parameters...
  1237.  
  1238.                 results-->0 = action_to_be;
  1239.                 results-->1 = parameters;
  1240.  
  1241. !  ...reverse first and second parameters if need be...
  1242.  
  1243.                 if (action_reversed && parameters==2)
  1244.                 {   i = results-->2; results-->2 = results-->3;
  1245.                     results-->3 = i;
  1246.                     if (nsns == 2)
  1247.                     {   i = special_number1; special_number1=special_number2;
  1248.                         special_number2=i;
  1249.                     }
  1250.                 }
  1251.  
  1252. !  ...and to reset "it"-style objects to the first of these parameters, if
  1253. !  there is one (and it really is an object)...
  1254.  
  1255.                 if (parameters > 0 && results-->2 >= 2)
  1256.                     PronounNotice(results-->2);
  1257.  
  1258. !  ...and worry about the case where an object was allowed as a parameter
  1259. !  even though the player wasn't holding it and should have been: in this
  1260. !  event, keep the results for next time round, go into "not holding" mode,
  1261. !  and for now tell the player what's happening and return a "take" request
  1262. !  instead...
  1263.  
  1264.                 if (not_holding~=0 && actor==player)
  1265.                 {   notheld_mode=1;
  1266.                     for (i=0:i<8:i++) kept_results-->i = results-->i;
  1267.                     results-->0 = ##Take;
  1268.                     results-->1 = 1;
  1269.                     results-->2 = not_holding;
  1270.                     L__M(##Miscellany, 26, not_holding);
  1271.                 }
  1272.  
  1273. !  (Notice that implicit takes are only generated for the player, and not
  1274. !  for other actors.  This avoids entirely logical, but misleading, text
  1275. !  being printed.)
  1276.  
  1277. !  ...and return from the parser altogether, having successfully matched
  1278. !  a line.
  1279.  
  1280.                 if (held_back_mode==1) { wn=hb_wn; jump LookForMore; }
  1281.                 rtrue;
  1282.             }
  1283.         }
  1284.  
  1285. !  The line has failed to match.
  1286. !  We continue the outer "for" loop, trying the next line in the grammar.
  1287.  
  1288.         if (etype>best_etype) best_etype=etype;
  1289.         if (etype~=ASKSCOPE_PE && etype>nextbest_etype) nextbest_etype=etype;
  1290.  
  1291. !  ...unless the line was something like "take all" which failed because
  1292. !  nothing matched the "all", in which case we stop and give an error now.
  1293.  
  1294.         if (take_all_rule == 2 && etype==NOTHING_PE) break;
  1295.    }
  1296.  
  1297. !  The grammar is exhausted: every line has failed to match.
  1298.  
  1299. !  **** (H) ****
  1300.  
  1301.   .GiveError;
  1302.         etype=best_etype;
  1303.  
  1304. !  Errors are handled differently depending on who was talking.
  1305.  
  1306. !  If the command was addressed to somebody else (eg, "dwarf, sfgh") then
  1307. !  it is taken as conversation which the parser has no business in disallowing.
  1308.  
  1309.     if (actor~=player)
  1310.     {   if (usual_grammar_after>0)
  1311.         {   verb_wordnum = usual_grammar_after;
  1312.             jump AlmostReParse;
  1313.         }
  1314.         wn=verb_wordnum;
  1315.         special_word=NextWord();
  1316.         if (special_word==comma_word)
  1317.         {   special_word=NextWord();
  1318.             verb_wordnum++;
  1319.         }
  1320.         special_number=TryNumber(verb_wordnum);
  1321.         results-->0=##NotUnderstood;
  1322.         results-->1=2;
  1323.         results-->2=1; special_number1=special_word;
  1324.         results-->3=actor;
  1325.         consult_from = verb_wordnum; consult_words = num_words-consult_from+1;
  1326.         rtrue;
  1327.     }
  1328.  
  1329. !  **** (I) ****
  1330.  
  1331. !  If the player was the actor (eg, in "take dfghh") the error must be printed,
  1332. !  and fresh input called for.  In three cases the oops word must be jiggled.
  1333.  
  1334.     if (ParserError(etype)~=0) jump ReType;
  1335.     pronoun_word = pronoun__word; pronoun_obj = pronoun__obj;
  1336.  
  1337.     if (etype==STUCK_PE)   { L__M(##Miscellany, 27); oops_from=1; }
  1338.     if (etype==UPTO_PE)    { L__M(##Miscellany, 28);
  1339.                              for (m=0:m<32:m++) pattern-->m = pattern2-->m;
  1340.                              pcount=pcount2; PrintCommand(0); print ".^";
  1341.                            }
  1342.     if (etype==NUMBER_PE)  L__M(##Miscellany, 29);
  1343.     if (etype==CANTSEE_PE) { L__M(##Miscellany, 30); oops_from=saved_oops; }
  1344.     if (etype==TOOLIT_PE)  L__M(##Miscellany, 31);
  1345.     if (etype==NOTHELD_PE) { L__M(##Miscellany, 32); oops_from=saved_oops; }
  1346.     if (etype==MULTI_PE)   L__M(##Miscellany, 33);
  1347.     if (etype==MMULTI_PE)  L__M(##Miscellany, 34);
  1348.     if (etype==VAGUE_PE)   L__M(##Miscellany, 35);
  1349.     if (etype==EXCEPT_PE)  L__M(##Miscellany, 36);
  1350.     if (etype==ANIMA_PE)   L__M(##Miscellany, 37);
  1351.     if (etype==VERB_PE)    L__M(##Miscellany, 38);
  1352.     if (etype==SCENERY_PE) L__M(##Miscellany, 39);
  1353.     if (etype==ITGONE_PE)
  1354.     {   if (pronoun_obj == NULL) L__M(##Miscellany, 35);
  1355.                             else L__M(##Miscellany, 40);
  1356.     }
  1357.     if (etype==JUNKAFTER_PE) L__M(##Miscellany, 41);
  1358.     if (etype==TOOFEW_PE)  L__M(##Miscellany, 42, multi_had);
  1359.     if (etype==NOTHING_PE) { if (multi_wanted==100) L__M(##Miscellany, 43);
  1360.                              else L__M(##Miscellany, 44);  }
  1361.  
  1362.     if (etype==ASKSCOPE_PE)
  1363.     {   scope_stage=3;
  1364.         if (indirect(scope_error)==-1)
  1365.         {   best_etype=nextbest_etype; jump GiveError;  }
  1366.     }
  1367.  
  1368. !  **** (J) ****
  1369.  
  1370. !  And go (almost) right back to square one...
  1371.  
  1372.     jump ReType;
  1373.  
  1374. !  ...being careful not to go all the way back, to avoid infinite repetition
  1375. !  of a deferred command causing an error.
  1376.  
  1377.  
  1378. !  **** (K) ****
  1379.  
  1380. !  At this point, the return value is all prepared, and we are only looking
  1381. !  to see if there is a "then" followed by subsequent instruction(s).
  1382.     
  1383.    .LookForMore;
  1384.  
  1385.    if (wn>num_words) rtrue;
  1386.  
  1387.    i=NextWord();
  1388.    if (i==THEN1__WD or THEN2__WD or THEN3__WD or comma_word)
  1389.    {   if (wn>num_words)
  1390.        {   held_back_mode = false; return; }
  1391.        i = WordAddress(verb_wordnum);
  1392.        j = WordAddress(wn);
  1393.        for (:i<j:i++) i->0 = ' ';
  1394.        i = NextWord();
  1395.        if (i==AGAIN1__WD or AGAIN2__WD or AGAIN3__WD)
  1396.        {   !   Delete the words "then again" from the again buffer,
  1397.            !   in which we have just realised that it must occur:
  1398.            !   prevents an infinite loop on "i. again"
  1399.  
  1400.            i = WordAddress(wn-2)-buffer;
  1401.            if (wn > num_words) j = 119; else j = WordAddress(wn)-buffer;
  1402.            for (:i<j:i++) buffer3->i = ' ';
  1403.        }
  1404.        Tokenise__(buffer,parse); held_back_mode = true; return;
  1405.    }
  1406.    best_etype=UPTO_PE; jump GiveError;
  1407. ];
  1408.  
  1409. [ ScopeCeiling person act;
  1410.   act = parent(person);
  1411.   while (parent(act)~=0
  1412.          && (act has transparent || act has supporter
  1413.              || (act has container && act has open)))
  1414.       act = parent(act);
  1415.   if (person == player && location == thedark) return thedark;
  1416.   return act;
  1417. ];
  1418.  
  1419. ! ----------------------------------------------------------------------------
  1420. !  Descriptors()
  1421. !
  1422. !  Handles descriptive words like "my", "his", "another" and so on.
  1423. !  Skips "the", and leaves wn pointing to the first misunderstood word.
  1424. !
  1425. !  Allowed to set up for a plural only if allow_p is set
  1426. !
  1427. !  Returns error number, or 0 if no error occurred
  1428. ! ----------------------------------------------------------------------------
  1429.  
  1430. Constant OTHER_BIT  =   1;     !  These will be used in Adjudicate()
  1431. Constant MY_BIT     =   2;     !  to disambiguate choices
  1432. Constant THAT_BIT   =   4;
  1433. Constant PLURAL_BIT =   8;
  1434. Constant LIT_BIT    =  16;
  1435. Constant UNLIT_BIT  =  32;
  1436.  
  1437. [ ResetDescriptors;
  1438.    indef_mode=0; indef_type=0; indef_wanted=0; indef_guess_p=0;
  1439.    indef_possambig = false;
  1440.    indef_owner = nothing;
  1441.    indef_cases = $$111111111111;
  1442.    indef_nspec_at = 0;
  1443. ];
  1444.  
  1445. [ Descriptors allow_multiple  o x flag cto type n;
  1446.  
  1447.    ResetDescriptors();
  1448.    if (wn > num_words) return 0;
  1449.  
  1450.    for (flag=true:flag:)
  1451.    {   o=NextWordStopped(); flag=false;
  1452.  
  1453.        for (x=1:x<=LanguageDescriptors-->0:x=x+4)
  1454.            if (o == LanguageDescriptors-->x)
  1455.            {   flag = true;
  1456.                type = LanguageDescriptors-->(x+2);
  1457.                if (type ~= DEFART_PK) indef_mode = true;
  1458.                indef_possambig = true;
  1459.                indef_cases = indef_cases & (LanguageDescriptors-->(x+1));
  1460.  
  1461.                if (type == POSSESS_PK)
  1462.                {   cto = LanguageDescriptors-->(x+3);
  1463.                    switch(cto)
  1464.                    {  0: indef_type = indef_type | MY_BIT;
  1465.                       1: indef_type = indef_type | THAT_BIT;
  1466.                       default: indef_owner = PronounValue(cto);
  1467.                         if (indef_owner == NULL) indef_owner = InformParser;
  1468.                    }
  1469.                }
  1470.  
  1471.                if (type == light)
  1472.                    indef_type = indef_type | LIT_BIT;
  1473.                if (type == -light)
  1474.                    indef_type = indef_type | UNLIT_BIT;
  1475.            }
  1476.  
  1477.        if (o==OTHER1__WD or OTHER2__WD or OTHER3__WD)
  1478.                             { indef_mode=1; flag=1;
  1479.                               indef_type = indef_type | OTHER_BIT; }
  1480.        if (o==ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD)
  1481.                             { indef_mode=1; flag=1; indef_wanted=100;
  1482.                               if (take_all_rule == 1)
  1483.                                   take_all_rule = 2;
  1484.                               indef_type = indef_type | PLURAL_BIT; }
  1485.        if (allow_plurals && allow_multiple)
  1486.        {   n=NumberWord(o);
  1487.            if (n==1)        { indef_mode=1; flag=1; }
  1488.            if (n>1)         { indef_guess_p=1;
  1489.                               indef_mode=1; flag=1; indef_wanted=n;
  1490.                               indef_nspec_at=wn-1;
  1491.                               indef_type = indef_type | PLURAL_BIT; }
  1492.        }
  1493.        if (flag==1
  1494.            && NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD)
  1495.            wn--;  ! Skip 'of' after these
  1496.    }
  1497.    wn--;
  1498.    if ((indef_wanted > 0) && (~~allow_multiple)) return MULTI_PE;
  1499.    return 0;
  1500. ];
  1501.  
  1502. ! ----------------------------------------------------------------------------
  1503. !  CreatureTest: Will this person do for a "creature" token?
  1504. ! ----------------------------------------------------------------------------
  1505.  
  1506. [ CreatureTest obj;
  1507.   if (obj has animate) rtrue;
  1508.   if (obj hasnt talkable) rfalse;
  1509.   if (action_to_be == ##Ask or ##Answer or ##Tell or ##AskFor) rtrue;
  1510.   rfalse;
  1511. ];
  1512.  
  1513. [ PrepositionChain wd index;
  1514.   if (line_tdata-->index == wd) return wd;
  1515.   if ((line_token-->index)->0 & $20 == 0) return -1;
  1516.   do
  1517.   {   if (line_tdata-->index == wd) return wd;
  1518.       index++;
  1519.   }
  1520.   until ((line_token-->index == ENDIT_TOKEN)
  1521.          || (((line_token-->index)->0 & $10) == 0));
  1522.   return -1;
  1523. ];
  1524.  
  1525. ! ----------------------------------------------------------------------------
  1526. !  ParseToken: Parses the given token, from the current word number wn
  1527. !
  1528. !  Returns:
  1529. !    REPARSE_CODE for "reconstructed input, please re-parse from scratch"
  1530. !    1            for "token accepted"
  1531. !    0            for "token failed"
  1532. !
  1533. !  (A)            Analyse the token; handle all tokens not involving
  1534. !                 object lists and break down others into elementary tokens
  1535. !  (B)            Begin parsing an object list
  1536. !  (C)            Parse descriptors (articles, pronouns, etc.) in the list
  1537. !  (D)            Parse an object name
  1538. !  (E)            Parse connectives ("and", "but", etc.) and go back to (C)
  1539. !  (F)            Return the conclusion of parsing an object list
  1540. ! ----------------------------------------------------------------------------
  1541.  
  1542. [ ParseToken results token_n token
  1543.              l o i j k and_parity single_object desc_wn many_flag
  1544.              token_allows_multiple;
  1545.  
  1546.    token = line_token-->token_n;
  1547.    AnalyseToken(token);
  1548.  
  1549. !  **** (A) ****
  1550.  
  1551.    token_filter = 0;
  1552.    parser_inflection = name;
  1553.  
  1554.    switch(found_ttype)
  1555.    {   ELEMENTARY_TT:
  1556.            switch(found_tdata)
  1557.            {   SPECIAL_TOKEN:
  1558.                    l=TryNumber(wn);
  1559.                    special_word=NextWord();
  1560.                    #ifdef DEBUG;
  1561.                    if (l~=-1000)
  1562.                        if (parser_trace>=3)
  1563.                            print "  [Read special as the number ", l, "]^";
  1564.                    #endif;
  1565.                    if (l==-1000)
  1566.                    {   #ifdef DEBUG;
  1567.                        if (parser_trace>=3)
  1568.                          print "  [Read special word at word number ", wn, "]^";
  1569.                        #endif;
  1570.                        l = special_word;
  1571.                    }
  1572.                    special_number=l;
  1573.                    if (nsns==0) special_number1=l; else special_number2=l;
  1574.                    nsns++;
  1575.                    single_object=1; jump PassToken;
  1576.  
  1577.                NUMBER_TOKEN:
  1578.                    l=TryNumber(wn++);
  1579.                    if (l==-1000) { etype=NUMBER_PE; rfalse; }
  1580.                    #ifdef DEBUG;
  1581.                    if (parser_trace>=3) print "  [Read number as ", l, "]^";
  1582.                    #endif;
  1583.                    if (nsns++==0) special_number1=l; else special_number2=l;
  1584.                    single_object=1; jump PassToken;
  1585.  
  1586.                CREATURE_TOKEN:
  1587.                    if (action_to_be==##Answer or ##Ask or ##AskFor or ##Tell)
  1588.                        scope_reason = TALKING_REASON;
  1589.  
  1590.                TOPIC_TOKEN:
  1591.                    consult_from = wn;
  1592.                    if ((line_ttype-->(token_n+1) ~= PREPOSITION_TT)
  1593.                        && (line_token-->(token_n+1) ~= ENDIT_TOKEN))
  1594.                        RunTimeError(13);
  1595.                    do o=NextWordStopped();
  1596.                    until (o==-1 || PrepositionChain(o, token_n+1) ~= -1);
  1597.                    wn--;
  1598.                    consult_words = wn-consult_from;
  1599.                    if (consult_words==0) return 1;
  1600.                    if (action_to_be==##Ask or ##Answer or ##Tell)
  1601.                    {   o=wn; wn=consult_from; parsed_number=NextWord();
  1602.                        if (parsed_number=='the' && consult_words>1)
  1603.                            parsed_number=NextWord();
  1604.                        wn=o;
  1605.                        if (nsns++==0) special_number1 = parsed_number;
  1606.                        else special_number2 = parsed_number;
  1607.                        single_object = 1; jump PassToken;
  1608.                    }
  1609.                    return 1;
  1610.            }
  1611.  
  1612.        PREPOSITION_TT:
  1613.            #Iffalse Grammar__Version==1;
  1614. !  Is it an unnecessary alternative preposition, when a previous choice
  1615. !  has already been matched?
  1616.            if ((token->0) & $10) return 1;
  1617.            #Endif;
  1618.  
  1619. !  If we've run out of the player's input, but still have parameters to
  1620. !  specify, we go into "infer" mode, remembering where we are and the
  1621. !  preposition we are inferring...
  1622.  
  1623.            if (wn > num_words)
  1624.            {   if (inferfrom==0 && parameters<params_wanted)
  1625.                {   inferfrom = pcount; inferword = token;
  1626.                    pattern-->pcount = REPARSE_CODE + Dword__No(found_tdata);
  1627.                }
  1628.  
  1629. !  If we are not inferring, then the line is wrong...
  1630.  
  1631.                if (inferfrom==0) return 0;
  1632.  
  1633. !  If not, then the line is right but we mark in the preposition...
  1634.  
  1635.                pattern-->pcount = REPARSE_CODE + Dword__No(found_tdata);
  1636.                return 1;
  1637.            }
  1638.  
  1639.            o = NextWord();
  1640.  
  1641.            pattern-->pcount = REPARSE_CODE + Dword__No(o);
  1642.  
  1643. !  Whereas, if the player has typed something here, see if it is the
  1644. !  required preposition... if it's wrong, the line must be wrong,
  1645. !  but if it's right, the token is passed (jump to finish this token).
  1646.  
  1647.            if (o == found_tdata) return 1;
  1648.            #Iffalse Grammar__Version==1;
  1649.            if (PrepositionChain(o, token_n) ~= -1) return 1;
  1650.            #Endif;
  1651.            return 0;
  1652.  
  1653.        GPR_TT:
  1654.            l=indirect(found_tdata);
  1655.            #ifdef DEBUG;
  1656.            if (parser_trace>=3)
  1657.                print "  [Outside parsing routine returned ", l, "]^";
  1658.            #endif;
  1659.            if (l<-200) { found_tdata = l + 256; break; }
  1660.            if (l<0) rfalse;
  1661.            if (l==GPR_PREPOSITION)
  1662.            {   params_wanted--; rtrue; }
  1663.            if (l==GPR_NUMBER)
  1664.            {   if (nsns==0) special_number1=parsed_number;
  1665.                else special_number2=parsed_number;
  1666.                nsns++;
  1667.            }
  1668.            if (l==GPR_REPARSE) return l;
  1669.            single_object=l; jump PassToken;
  1670.  
  1671.        SCOPE_TT:
  1672.            scope_token = found_tdata;
  1673.            scope_stage = 1;
  1674.            l = indirect(scope_token);
  1675.            #ifdef DEBUG;
  1676.            if (parser_trace>=3)
  1677.                print "  [Scope routine returned multiple-flag of ", l, "]^";
  1678.            #endif;
  1679.            if (l==1) found_tdata = MULTI_TOKEN; else found_tdata = NOUN_TOKEN;
  1680.  
  1681.        ATTR_FILTER_TT:
  1682.            token_filter = 1 + found_tdata;
  1683.            found_tdata = NOUN_TOKEN;
  1684.  
  1685.        ROUTINE_FILTER_TT:
  1686.            token_filter = found_tdata;
  1687.            found_tdata = NOUN_TOKEN;
  1688.    }
  1689.  
  1690.    token = found_tdata;
  1691.  
  1692. !  **** (B) ****
  1693.  
  1694. !  There are now three possible ways we can be here:
  1695. !      parsing an elementary token other than "special" or "number";
  1696. !      parsing a scope token;
  1697. !      parsing a noun-filter token (either by routine or attribute).
  1698. !
  1699. !  In each case, token holds the type of elementary parse to
  1700. !  perform in matching one or more objects, and
  1701. !  token_filter is 0 (default), an attribute + 1 for an attribute filter
  1702. !  or a routine address for a routine filter.
  1703.  
  1704.    token_allows_multiple = false;
  1705.    if (token == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
  1706.                 or MULTIINSIDE_TOKEN) token_allows_multiple = true;
  1707.  
  1708.    many_flag = false; and_parity = true; dont_infer = false;
  1709.  
  1710. !  **** (C) ****
  1711. !  We expect to find a list of objects next in what the player's typed.
  1712.  
  1713.   .ObjectList;
  1714.  
  1715.    #ifdef DEBUG;
  1716.    if (parser_trace>=3) print "  [Object list from word ", wn, "]^";
  1717.    #endif;
  1718.  
  1719. !  Take an advance look at the next word: if it's "it" or "them", and these
  1720. !  are unset, set the appropriate error number and give up on the line
  1721. !  (if not, these are still parsed in the usual way - it is not assumed
  1722. !  that they still refer to something in scope)
  1723.  
  1724.     o=NextWord(); wn--;
  1725.  
  1726.     pronoun_word = NULL; pronoun_obj = NULL;
  1727.     l = PronounValue(o);
  1728.     if (l ~= 0)
  1729.     {   pronoun_word = o; pronoun_obj = l;
  1730.         if (l == NULL)
  1731.         {   !   Don't assume this is a use of an unset pronoun until the
  1732.             !   descriptors have been checked, because it might be an
  1733.             !   article (or some such) instead
  1734.  
  1735.             for (l=1:l<=LanguageDescriptors:l=l+4)
  1736.                 if (o == LanguageDescriptors-->l) jump AssumeDescriptor;
  1737.             pronoun__word=pronoun_word; pronoun__obj=pronoun_obj;
  1738.             etype=VAGUE_PE; return 0;
  1739.         }
  1740.     }
  1741.  
  1742.     .AssumeDescriptor;
  1743.  
  1744.     if (o==ME1__WD or ME2__WD or ME3__WD)
  1745.     {   pronoun_word = o; pronoun_obj = player;
  1746.     }
  1747.  
  1748.     allow_plurals = true; desc_wn = wn;
  1749.  
  1750.     .TryAgain;
  1751. !   First, we parse any descriptive words (like "the", "five" or "every"):
  1752.     l = Descriptors(token_allows_multiple); if (l~=0) { etype=l; return 0; }
  1753.  
  1754.     .TryAgain2;
  1755.  
  1756. !  **** (D) ****
  1757.  
  1758. !  This is an actual specified object, and is therefore where a typing error
  1759. !  is most likely to occur, so we set:
  1760.  
  1761.     oops_from = wn;
  1762.  
  1763. !  So, two cases.  Case 1: token not equal to "held" (so, no implicit takes)
  1764. !  but we may well be dealing with multiple objects
  1765.  
  1766. !  In either case below we use NounDomain, giving it the token number as
  1767. !  context, and two places to look: among the actor's possessions, and in the
  1768. !  present location.  (Note that the order depends on which is likeliest.)
  1769.  
  1770.     if (token ~= HELD_TOKEN)
  1771.     {   i=multiple_object-->0;
  1772.         #ifdef DEBUG;
  1773.         if (parser_trace>=3)
  1774.             print "  [Calling NounDomain on location and actor]^";
  1775.         #endif;
  1776.         l=NounDomain(actors_location, actor, token);
  1777.         if (l==REPARSE_CODE) return l;                  ! Reparse after Q&A
  1778.         if (l==0) {   if (indef_possambig)
  1779.                       {   ResetDescriptors(); wn = desc_wn; jump TryAgain2; }
  1780.                       etype=CantSee(); jump FailToken; } ! Choose best error
  1781.  
  1782.         #ifdef DEBUG;
  1783.         if (parser_trace>=3)
  1784.         {   if (l>1)
  1785.                 print "  [ND returned ", (the) l, "]^";
  1786.             else
  1787.             {   print "  [ND appended to the multiple object list:^";
  1788.                 k=multiple_object-->0;
  1789.                 for (j=i+1:j<=k:j++)
  1790.                     print "  Entry ", j, ": ", (The) multiple_object-->j,
  1791.                           " (", multiple_object-->j, ")^";
  1792.                 print "  List now has size ", k, "]^";
  1793.             }
  1794.         }
  1795.         #endif;
  1796.  
  1797.         if (l==1)
  1798.         {   if (~~many_flag)
  1799.             {   many_flag = true;
  1800.             }
  1801.             else                                  ! Merge with earlier ones
  1802.             {   k=multiple_object-->0;            ! (with either parity)
  1803.                 multiple_object-->0 = i;
  1804.                 for (j=i+1:j<=k:j++)
  1805.                 {   if (and_parity) MultiAdd(multiple_object-->j);
  1806.                     else MultiSub(multiple_object-->j);
  1807.                 }
  1808.                 #ifdef DEBUG;
  1809.                 if (parser_trace>=3)
  1810.                     print "  [Merging ", k-i, " new objects to the ",
  1811.                         i, " old ones]^";
  1812.                 #endif;
  1813.             }
  1814.         }
  1815.         else
  1816.         {   ! A single object was indeed found
  1817.  
  1818.             if (match_length == 0 && indef_possambig)
  1819.             {   !   So the answer had to be inferred from no textual data,
  1820.                 !   and we know that there was an ambiguity in the descriptor
  1821.                 !   stage (such as a word which could be a pronoun being
  1822.                 !   parsed as an article or possessive).  It's worth having
  1823.                 !   another go.
  1824.  
  1825.                 ResetDescriptors(); wn = desc_wn; jump TryAgain2;
  1826.             }
  1827.         
  1828.             if (token==CREATURE_TOKEN && CreatureTest(l)==0)
  1829.             {   etype=ANIMA_PE; jump FailToken; } !  Animation is required
  1830.  
  1831.             if (~~many_flag)
  1832.                 single_object = l;
  1833.             else
  1834.             {   if (and_parity) MultiAdd(l); else MultiSub(l);
  1835.                 #ifdef DEBUG;
  1836.                 if (parser_trace>=3)
  1837.                     print "  [Combining ", (the) l, " with list]^";
  1838.                 #endif;
  1839.             }
  1840.         }
  1841.     }
  1842.  
  1843. !  Case 2: token is "held" (which fortunately can't take multiple objects)
  1844. !  and may generate an implicit take
  1845.  
  1846.     else
  1847.  
  1848.     {   l=NounDomain(actor,actors_location,token);       ! Same as above...
  1849.         if (l==REPARSE_CODE) return l;
  1850.         if (l==0)
  1851.         {   if (indef_possambig)
  1852.             {   ResetDescriptors(); wn = desc_wn; jump TryAgain2; }
  1853.             etype=CantSee(); return l;                   ! Choose best error
  1854.         }
  1855.  
  1856. !  ...until it produces something not held by the actor.  Then an implicit
  1857. !  take must be tried.  If this is already happening anyway, things are too
  1858. !  confused and we have to give up (but saving the oops marker so as to get
  1859. !  it on the right word afterwards).
  1860. !  The point of this last rule is that a sequence like
  1861. !
  1862. !      > read newspaper
  1863. !      (taking the newspaper first)
  1864. !      The dwarf unexpectedly prevents you from taking the newspaper!
  1865. !
  1866. !  should not be allowed to go into an infinite repeat - read becomes
  1867. !  take then read, but take has no effect, so read becomes take then read...
  1868. !  Anyway for now all we do is record the number of the object to take.
  1869.  
  1870.         o=parent(l);
  1871.         if (o~=actor)
  1872.         {   if (notheld_mode==1)
  1873.             {   saved_oops=oops_from; etype=NOTHELD_PE; jump FailToken;
  1874.             }
  1875.             not_holding = l;
  1876.             #ifdef DEBUG;
  1877.             if (parser_trace>=3)
  1878.                 print "  [Allowing object ", (the) l, " for now]^";
  1879.             #endif;
  1880.         }
  1881.         single_object = l;
  1882.     }
  1883.  
  1884. !  The following moves the word marker to just past the named object...
  1885.  
  1886.     wn = oops_from + match_length;
  1887.  
  1888. !  **** (E) ****
  1889.  
  1890. !  Object(s) specified now: is that the end of the list, or have we reached
  1891. !  "and", "but" and so on?  If so, create a multiple-object list if we
  1892. !  haven't already (and are allowed to).
  1893.  
  1894.     .NextInList;
  1895.  
  1896.     o=NextWord();
  1897.  
  1898.     if (o==AND1__WD or AND2__WD or AND3__WD or BUT1__WD or BUT2__WD or BUT3__WD
  1899.            or comma_word)
  1900.     {
  1901.         #ifdef DEBUG;
  1902.         if (parser_trace>=3) print "  [Read connective '", (address) o, "']^";
  1903.         #endif;
  1904.  
  1905.         if (~~token_allows_multiple)
  1906.         {   etype=MULTI_PE; jump FailToken;
  1907.         }
  1908.  
  1909.         if (o==BUT1__WD or BUT2__WD or BUT3__WD) and_parity = 1-and_parity;
  1910.  
  1911.         if (~~many_flag)
  1912.         {   multiple_object-->0 = 1;
  1913.             multiple_object-->1 = single_object;
  1914.             many_flag = true;
  1915.             #ifdef DEBUG;
  1916.             if (parser_trace>=3)
  1917.                 print "  [Making new list from ", (the) single_object, "]^";
  1918.             #endif;
  1919.         }
  1920.         dont_infer = true; inferfrom=0;           ! Don't print (inferences)
  1921.         jump ObjectList;                          ! And back around
  1922.     }
  1923.  
  1924.     wn--;   ! Word marker back to first not-understood word
  1925.  
  1926. !  **** (F) ****
  1927.  
  1928. !  Happy or unhappy endings:
  1929.  
  1930.     .PassToken;
  1931.  
  1932.     if (many_flag)
  1933.     {   single_object = 0;
  1934.         multi_context = token;
  1935.     }
  1936.     else
  1937.     {   if (indef_mode==1 && indef_type & PLURAL_BIT ~= 0)
  1938.         {   if (indef_wanted<100 && indef_wanted>1)
  1939.             {   multi_had=1; multi_wanted=indef_wanted;
  1940.                 etype=TOOFEW_PE;
  1941.                 jump FailToken;
  1942.             }
  1943.         }
  1944.     }
  1945.     results-->(parameters+2) = single_object;
  1946.     parameters++;
  1947.     pattern-->pcount = single_object;
  1948.     return 1;
  1949.  
  1950.     .FailToken;
  1951.  
  1952. !  If we were only guessing about it being a plural, try again but only
  1953. !  allowing singulars (so that words like "six" are not swallowed up as
  1954. !  Descriptors)
  1955.  
  1956.     if (allow_plurals && indef_guess_p==1)
  1957.     {   allow_plurals=false; wn=desc_wn; jump TryAgain;
  1958.     }
  1959.     return 0;
  1960. ];
  1961.  
  1962. ! ----------------------------------------------------------------------------
  1963. !  NounDomain does the most substantial part of parsing an object name.
  1964. !
  1965. !  It is given two "domains" - usually a location and then the actor who is
  1966. !  looking - and a context (i.e. token type), and returns:
  1967. !
  1968. !   0    if no match at all could be made,
  1969. !   1    if a multiple object was made,
  1970. !   k    if object k was the one decided upon,
  1971. !   REPARSE_CODE if it asked a question of the player and consequently rewrote
  1972. !        the player's input, so that the whole parser should start again
  1973. !        on the rewritten input.
  1974. !
  1975. !   In the case when it returns 1<k<REPARSE_CODE, it also sets the variable
  1976. !   length_of_noun to the number of words in the input text matched to the
  1977. !   noun.
  1978. !   In the case k=1, the multiple objects are added to multiple_object by
  1979. !   hand (not by MultiAdd, because we want to allow duplicates).
  1980. ! ----------------------------------------------------------------------------
  1981.  
  1982. [ NounDomain domain1 domain2 context    first_word i j k l
  1983.                                         answer_words marker;
  1984.  
  1985. #ifdef DEBUG;
  1986.   if (parser_trace>=4)
  1987.   {   print "   [NounDomain called at word ", wn, "^";
  1988.       print "   ";
  1989.       if (indef_mode)
  1990.       {   print "seeking indefinite object: ";
  1991.           if (indef_type & OTHER_BIT)  print "other ";
  1992.           if (indef_type & MY_BIT)     print "my ";
  1993.           if (indef_type & THAT_BIT)   print "that ";
  1994.           if (indef_type & PLURAL_BIT) print "plural ";
  1995.           if (indef_type & LIT_BIT)    print "lit ";
  1996.           if (indef_type & UNLIT_BIT)  print "unlit ";
  1997.           if (indef_owner ~= 0) print "owner:", (name) indef_owner;
  1998.           new_line;
  1999.           print "   number wanted: ";
  2000.           if (indef_wanted == 100) print "all"; else print indef_wanted;
  2001.           new_line;
  2002.           print "   most likely GNAs of names: ", indef_cases, "^";
  2003.       }
  2004.       else print "seeking definite object^";
  2005.   }
  2006. #endif;
  2007.  
  2008.   match_length=0; number_matched=0; match_from=wn; placed_in_flag=0;
  2009.  
  2010.   SearchScope(domain1, domain2, context);
  2011.  
  2012. #ifdef DEBUG;
  2013.   if (parser_trace>=4) print "   [ND made ", number_matched, " matches]^";
  2014. #endif;
  2015.  
  2016.   wn=match_from+match_length;
  2017.  
  2018. !  If nothing worked at all, leave with the word marker skipped past the
  2019. !  first unmatched word...
  2020.  
  2021.   if (number_matched==0) { wn++; rfalse; }
  2022.  
  2023. !  Suppose that there really were some words being parsed (i.e., we did
  2024. !  not just infer).  If so, and if there was only one match, it must be
  2025. !  right and we return it...
  2026.  
  2027.   if (match_from <= num_words)
  2028.   {   if (number_matched==1) { i=match_list-->0; return i; }
  2029.  
  2030. !  ...now suppose that there was more typing to come, i.e. suppose that
  2031. !  the user entered something beyond this noun.  If nothing ought to follow,
  2032. !  then there must be a mistake, (unless what does follow is just a full
  2033. !  stop, and or comma)
  2034.  
  2035.       if (wn<=num_words)
  2036.       {   i=NextWord(); wn--;
  2037.           if (i ~=  AND1__WD or AND2__WD or AND3__WD or comma_word
  2038.                  or THEN1__WD or THEN2__WD or THEN3__WD
  2039.                  or BUT1__WD or BUT2__WD or BUT3__WD)
  2040.           {   if (lookahead==ENDIT_TOKEN) rfalse;
  2041.           }
  2042.       }
  2043.   }
  2044.  
  2045. !  Now look for a good choice, if there's more than one choice...
  2046.  
  2047.   number_of_classes=0;
  2048.   
  2049.   if (number_matched==1) i=match_list-->0;
  2050.   if (number_matched>1)
  2051.   {   i=Adjudicate(context);
  2052.       if (i==-1) rfalse;
  2053.       if (i==1) rtrue;       !  Adjudicate has made a multiple
  2054.                              !  object, and we pass it on
  2055.   }
  2056.  
  2057. !  If i is non-zero here, one of two things is happening: either
  2058. !  (a) an inference has been successfully made that object i is
  2059. !      the intended one from the user's specification, or
  2060. !  (b) the user finished typing some time ago, but we've decided
  2061. !      on i because it's the only possible choice.
  2062. !  In either case we have to keep the pattern up to date,
  2063. !  note that an inference has been made and return.
  2064. !  (Except, we don't note which of a pile of identical objects.)
  2065.  
  2066.   if (i~=0)
  2067.   {   if (dont_infer) return i;
  2068.       if (inferfrom==0) inferfrom=pcount;
  2069.       pattern-->pcount = i;
  2070.       return i;
  2071.   }
  2072.  
  2073. !  If we get here, there was no obvious choice of object to make.  If in
  2074. !  fact we've already gone past the end of the player's typing (which
  2075. !  means the match list must contain every object in scope, regardless
  2076. !  of its name), then it's foolish to give an enormous list to choose
  2077. !  from - instead we go and ask a more suitable question...
  2078.  
  2079.   if (match_from > num_words) jump Incomplete;
  2080.  
  2081. !  Now we print up the question, using the equivalence classes as worked
  2082. !  out by Adjudicate() so as not to repeat ourselves on plural objects...
  2083.  
  2084.   if (context==CREATURE_TOKEN)
  2085.       L__M(##Miscellany, 45); else L__M(##Miscellany, 46);
  2086.  
  2087.   j=number_of_classes; marker=0;
  2088.   for (i=1:i<=number_of_classes:i++)
  2089.   {   
  2090.       while (((match_classes-->marker) ~= i)
  2091.              && ((match_classes-->marker) ~= -i)) marker++;
  2092.       k=match_list-->marker;
  2093.  
  2094.       if (match_classes-->marker > 0) print (the) k; else print (a) k;
  2095.  
  2096.       if (i<j-1)  print ", ";
  2097.       if (i==j-1) print (string) OR__TX;
  2098.   }
  2099.   print "?^";
  2100.  
  2101. !  ...and get an answer:
  2102.  
  2103.   .WhichOne;
  2104.   for (i=2:i<120:i++) buffer2->i=' ';
  2105.   answer_words=Keyboard(buffer2, parse2);
  2106.  
  2107.   first_word=(parse2-->1);
  2108.  
  2109. !  Take care of "all", because that does something too clever here to do
  2110. !  later on:
  2111.  
  2112.   if (first_word == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD)
  2113.   {   
  2114.       if (context == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
  2115.                      or MULTIINSIDE_TOKEN)
  2116.       {   l=multiple_object-->0;
  2117.           for (i=0:i<number_matched && l+i<63:i++)
  2118.           {   k=match_list-->i;
  2119.               multiple_object-->(i+1+l) = k;
  2120.           }
  2121.           multiple_object-->0 = i+l;
  2122.           rtrue;
  2123.       }
  2124.       L__M(##Miscellany, 47);
  2125.       jump WhichOne;
  2126.   }
  2127.  
  2128. !  If the first word of the reply can be interpreted as a verb, then
  2129. !  assume that the player has ignored the question and given a new
  2130. !  command altogether.
  2131. !  (This is one time when it's convenient that the directions are
  2132. !  not themselves verbs - thus, "north" as a reply to "Which, the north
  2133. !  or south door" is not treated as a fresh command but as an answer.)
  2134.  
  2135.   #ifdef LanguageIsVerb;
  2136.   if (first_word==0)
  2137.   {   j = wn; first_word=LanguageIsVerb(buffer2, parse2, 1); wn = j;
  2138.   }
  2139.   #endif;
  2140.   if (first_word ~= 0)
  2141.   {   j=first_word->#dict_par1;
  2142.       if ((0~=j&1) && (first_word ~= 'long' or 'short' or 'normal'
  2143.                                      or 'brief' or 'full' or 'verbose'))
  2144.       {   CopyBuffer(buffer, buffer2);
  2145.           return REPARSE_CODE;
  2146.       }
  2147.   }
  2148.  
  2149. !  Now we insert the answer into the original typed command, as
  2150. !  words additionally describing the same object
  2151. !  (eg, > take red button
  2152. !       Which one, ...
  2153. !       > music
  2154. !  becomes "take music red button".  The parser will thus have three
  2155. !  words to work from next time, not two.)
  2156.  
  2157.   k = WordAddress(match_from) - buffer; l=buffer2->1+1;
  2158.   for (j=buffer + buffer->0 - 1: j>= buffer+k+l: j--)
  2159.       j->0 = 0->(j-l);
  2160.   for (i=0:i<l:i++) buffer->(k+i) = buffer2->(2+i);
  2161.   buffer->(k+l-1) = ' ';
  2162.   buffer->1 = buffer->1 + l;
  2163.   if (buffer->1 >= (buffer->0 - 1)) buffer->1 = buffer->0;
  2164.  
  2165. !  Having reconstructed the input, we warn the parser accordingly
  2166. !  and get out.
  2167.  
  2168.   return REPARSE_CODE;
  2169.  
  2170. !  Now we come to the question asked when the input has run out
  2171. !  and can't easily be guessed (eg, the player typed "take" and there
  2172. !  were plenty of things which might have been meant).
  2173.  
  2174.   .Incomplete;
  2175.  
  2176.   if (context==CREATURE_TOKEN)
  2177.       L__M(##Miscellany, 48); else L__M(##Miscellany, 49);
  2178.  
  2179.   for (i=2:i<120:i++) buffer2->i=' ';
  2180.   answer_words=Keyboard(buffer2, parse2);
  2181.  
  2182.   first_word=(parse2-->1);
  2183.   #ifdef LanguageIsVerb;
  2184.   if (first_word==0)
  2185.   {   j = wn; first_word=LanguageIsVerb(buffer2, parse2, 1); wn = j;
  2186.   }
  2187.   #endif;
  2188.  
  2189. !  Once again, if the reply looks like a command, give it to the
  2190. !  parser to get on with and forget about the question...
  2191.  
  2192.   if (first_word ~= 0)
  2193.   {   j=first_word->#dict_par1;
  2194.       if (0~=j&1)
  2195.       {   CopyBuffer(buffer, buffer2);
  2196.           return REPARSE_CODE;
  2197.       }
  2198.   }
  2199.  
  2200. !  ...but if we have a genuine answer, then:
  2201. !
  2202. !  (1) we must glue in text suitable for anything that's been inferred.
  2203.  
  2204.   if (inferfrom ~= 0)
  2205.   {   for (j = inferfrom: j<pcount: j++)
  2206.       {   if (pattern-->j == PATTERN_NULL) continue;
  2207.           i=2+buffer->1; (buffer->1)++; buffer->(i++) = ' ';
  2208.     
  2209.           if (parser_trace >= 5)
  2210.           print "[Gluing in inference with pattern code ", pattern-->j, "]^";
  2211.  
  2212.           parse2-->1 = 0;
  2213.  
  2214.           ! An inferred object.  Best we can do is glue in a pronoun.
  2215.           ! (This is imperfect, but it's very seldom needed anyway.)
  2216.     
  2217.           if (pattern-->j >= 2 && pattern-->j < REPARSE_CODE)
  2218.           {   PronounNotice(pattern-->j);
  2219.               for (k=1: k<=LanguagePronouns-->0: k=k+3)
  2220.                   if (pattern-->j == LanguagePronouns-->(k+2))
  2221.                   {   parse2-->1 = LanguagePronouns-->k;
  2222.                       if (parser_trace >= 5)
  2223.                       print "[Using pronoun '", (address) parse2-->1, "']^";
  2224.                       break;
  2225.                   }
  2226.           }
  2227.           else
  2228.           {   ! An inferred preposition.
  2229.               parse2-->1 = No__Dword(pattern-->j - REPARSE_CODE);
  2230.               if (parser_trace >= 5)
  2231.                   print "[Using preposition '", (address) parse2-->1, "']^";
  2232.           }
  2233.     
  2234.           ! parse2-->1 now holds the dictionary address of the word to glue in.
  2235.     
  2236.           if (parse2-->1 ~= 0)
  2237.           {   k = buffer + i;
  2238.               @output_stream 3 k;
  2239.               print (address) parse2-->1;
  2240.               @output_stream -3;
  2241.               k = k-->0;
  2242.               for (l=i:l<i+k:l++) buffer->l = buffer->(l+2);
  2243.               i = i + k; buffer->1 = i-2;
  2244.           }
  2245.       }
  2246.   }
  2247.  
  2248. !  (2) we must glue the newly-typed text onto the end.
  2249.  
  2250.   i=2+buffer->1; (buffer->1)++; buffer->(i++) = ' ';
  2251.   for (j=0: j<buffer2->1: i++, j++)
  2252.   {   buffer->i = buffer2->(j+2);
  2253.       (buffer->1)++;
  2254.       if (buffer->1 == 120) break;
  2255.   }    
  2256.  
  2257. !  (3) we fill up the buffer with spaces, which is unnecessary, but may
  2258. !      help incorrectly-written interpreters to cope.
  2259.  
  2260.   for (:i<120:i++) buffer->i = ' ';
  2261.  
  2262.   return REPARSE_CODE;
  2263. ];
  2264.  
  2265. ! ----------------------------------------------------------------------------
  2266. !  The Adjudicate routine tries to see if there is an obvious choice, when
  2267. !  faced with a list of objects (the match_list) each of which matches the
  2268. !  player's specification equally well.
  2269. !
  2270. !  To do this it makes use of the context (the token type being worked on).
  2271. !  It counts up the number of obvious choices for the given context
  2272. !  (all to do with where a candidate is, except for 6 (animate) which is to
  2273. !  do with whether it is animate or not);
  2274. !
  2275. !  if only one obvious choice is found, that is returned;
  2276. !
  2277. !  if we are in indefinite mode (don't care which) one of the obvious choices
  2278. !    is returned, or if there is no obvious choice then an unobvious one is
  2279. !    made;
  2280. !
  2281. !  at this stage, we work out whether the objects are distinguishable from
  2282. !    each other or not: if they are all indistinguishable from each other,
  2283. !    then choose one, it doesn't matter which;
  2284. !
  2285. !  otherwise, 0 (meaning, unable to decide) is returned (but remember that
  2286. !    the equivalence classes we've just worked out will be needed by other
  2287. !    routines to clear up this mess, so we can't economise on working them
  2288. !    out).
  2289. !
  2290. !  Returns -1 if an error occurred
  2291. ! ----------------------------------------------------------------------------
  2292.  
  2293. [ Adjudicate context i j k good_ones last n ultimate flag offset;
  2294.  
  2295. #ifdef DEBUG;
  2296.   if (parser_trace>=4)
  2297.   {   print "   [Adjudicating match list of size ", number_matched,
  2298.           " in context ", context, "^";
  2299.       print "   ";
  2300.       if (indef_mode)
  2301.       {   print "indefinite type: ";
  2302.           if (indef_type & OTHER_BIT)  print "other ";
  2303.           if (indef_type & MY_BIT)     print "my ";
  2304.           if (indef_type & THAT_BIT)   print "that ";
  2305.           if (indef_type & PLURAL_BIT) print "plural ";
  2306.           if (indef_type & LIT_BIT)    print "lit ";
  2307.           if (indef_type & UNLIT_BIT)  print "unlit ";
  2308.           if (indef_owner ~= 0) print "owner:", (name) indef_owner;
  2309.           new_line;
  2310.           print "   number wanted: ";
  2311.           if (indef_wanted == 100) print "all"; else print indef_wanted;
  2312.           new_line;
  2313.           print "   most likely GNAs of names: ", indef_cases, "^";
  2314.       }
  2315.       else print "definite object^";
  2316.   }
  2317. #endif;
  2318.  
  2319.   j=number_matched-1; good_ones=0; last=match_list-->0;
  2320.   for (i=0:i<=j:i++)
  2321.   {   n=match_list-->i;
  2322.       match_scores-->i = good_ones;
  2323.  
  2324.       ultimate=n;
  2325.       do
  2326.           ultimate=parent(ultimate);
  2327.       until (ultimate==actors_location or actor or 0);
  2328.  
  2329. !      if (context==NOUN_TOKEN && ultimate==actors_location
  2330. !          && n~=actor && n hasnt concealed && n hasnt scenery &&
  2331. !          (token_filter==0 || UserFilter(n)==1)) { good_ones++; last=n; }
  2332.       if (context==HELD_TOKEN && parent(n)==actor)
  2333.       {   good_ones++; last=n; }
  2334.       if (context==MULTI_TOKEN && ultimate==actors_location
  2335.           && n~=actor && n hasnt concealed && n hasnt scenery) 
  2336.       {   good_ones++; last=n; }
  2337.       if (context==MULTIHELD_TOKEN && parent(n)==actor)
  2338.       {   good_ones++; last=n; }
  2339.  
  2340.       if (context==MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN)
  2341.       {   if (advance_warning==-1)
  2342.           {   if (context==MULTIEXCEPT_TOKEN)
  2343.               {   good_ones++; last=n;
  2344.               }
  2345.               if (context==MULTIINSIDE_TOKEN)
  2346.               {   if (parent(n)~=actor) { good_ones++; last=n; }
  2347.               }
  2348.           }
  2349.           else
  2350.           {   if (context==MULTIEXCEPT_TOKEN && n~=advance_warning)
  2351.               {   good_ones++; last=n; }
  2352.               if (context==MULTIINSIDE_TOKEN && n in advance_warning)
  2353.               {   good_ones++; last=n; }
  2354.           }
  2355.       }
  2356.       if (context==CREATURE_TOKEN && CreatureTest(n)==1)
  2357.       {   good_ones++; last=n; }
  2358.  
  2359.       match_scores-->i = 1000*(good_ones - match_scores-->i);
  2360.  
  2361.       #ifdef DEBUG;
  2362.       if (parser_trace>=4)
  2363.       {   print "   ML: ", (name) n, " ";
  2364.           print " initial score ", match_scores-->i, "^";
  2365.       }
  2366.       #endif;
  2367.   }
  2368.   if (good_ones==1) return last;
  2369.  
  2370.   ! If there is ambiguity about what was typed, but it definitely wasn't
  2371.   ! animate as required, then return anything; higher up in the parser
  2372.   ! a suitable error will be given.  (This prevents a question being asked.)
  2373.   !
  2374.   if (context==CREATURE_TOKEN && good_ones==0) return match_list-->0;
  2375.  
  2376.   if (indef_mode==0) indef_type=0;
  2377.  
  2378.   ScoreMatchL();
  2379.   if (number_matched == 0) return -1;
  2380.  
  2381.   if (indef_mode==1 && indef_type & PLURAL_BIT ~= 0)
  2382.   {   if (context ~= MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
  2383.                      or MULTIINSIDE_TOKEN)
  2384.       {   etype=MULTI_PE; return -1; }
  2385.       i=0; offset=multiple_object-->0;
  2386.       for (j=BestGuess():j~=-1 && i<indef_wanted
  2387.            && i+offset<63:j=BestGuess())
  2388.       {   flag=0;
  2389.           if (j hasnt concealed && j hasnt worn) flag=1;
  2390.           if (context==MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
  2391.               && parent(j)~=actor) flag=0;
  2392.           k=ChooseObjects(j,flag);
  2393.           if (k==1) flag=1; else { if (k==2) flag=0; }
  2394.           if (flag==1)
  2395.           {   i++; multiple_object-->(i+offset) = j;
  2396. #ifdef DEBUG;
  2397.               if (parser_trace>=4) print "   Accepting it^";
  2398. #endif;
  2399.           }
  2400.           else
  2401.           {   i=i;
  2402. #ifdef DEBUG;
  2403.               if (parser_trace>=4) print "   Rejecting it^";
  2404. #endif;
  2405.           }
  2406.       }
  2407.       if (i<indef_wanted && indef_wanted<100)
  2408.       {   etype=TOOFEW_PE; multi_wanted=indef_wanted;
  2409.           multi_had=multiple_object-->0;
  2410.           return -1;
  2411.       }
  2412.       multiple_object-->0 = i+offset;
  2413.       multi_context=context;
  2414. #ifdef DEBUG;
  2415.       if (parser_trace>=4)
  2416.           print "   Made multiple object of size ", i, "]^";
  2417. #endif;
  2418.       return 1;
  2419.   }
  2420.  
  2421.   for (i=0:i<number_matched:i++) match_classes-->i=0;
  2422.  
  2423.   n=1;
  2424.   for (i=0:i<number_matched:i++)
  2425.       if (match_classes-->i==0)
  2426.       {   match_classes-->i=n++; flag=0;
  2427.           for (j=i+1:j<number_matched:j++)
  2428.               if (match_classes-->j==0
  2429.                   && Identical(match_list-->i, match_list-->j)==1)
  2430.               {   flag=1;
  2431.                   match_classes-->j=match_classes-->i;
  2432.               }
  2433.           if (flag==1) match_classes-->i = 1-n;
  2434.       }
  2435.   n--; number_of_classes = n;
  2436.  
  2437. #ifdef DEBUG;
  2438.   if (parser_trace>=4)
  2439.   {   print "   Difficult adjudication with ", n, " equivalence classes:^";
  2440.       for (i=0:i<number_matched:i++)
  2441.           print "   ", (The) match_list-->i,
  2442.                 " (", match_list-->i, ")  ---  ",match_classes-->i, "^";
  2443.   }
  2444. #endif;
  2445.  
  2446.   if (n>1 && indef_mode==0)
  2447.   {   j=0; good_ones=0;
  2448.       for (i=0:i<number_matched:i++)
  2449.       {   k=ChooseObjects(match_list-->i,2);
  2450.           if (k==j) good_ones++;
  2451.           if (k>j) { j=k; good_ones=1; last=match_list-->i; }
  2452.       }
  2453.       if (good_ones==1)
  2454.       {
  2455. #ifdef DEBUG;
  2456.           if (parser_trace>=4)
  2457.               print "   ChooseObjects picked a best.]^";
  2458. #endif;
  2459.           return last;
  2460.       }
  2461. #ifdef DEBUG;
  2462.       if (parser_trace>=4)
  2463.           print "   Unable to decide: it's a draw.]^";
  2464. #endif;
  2465.       return 0;
  2466.   }
  2467.  
  2468. !  When the player is really vague, or there's a single collection of
  2469. !  indistinguishable objects to choose from, choose the one the player
  2470. !  most recently acquired, or if the player has none of them, then
  2471. !  the one most recently put where it is.
  2472.  
  2473.   if (n==1) dont_infer = true;
  2474.   return BestGuess();
  2475. ];
  2476.  
  2477. ! ----------------------------------------------------------------------------
  2478. !  ReviseMulti  revises the multiple object which already exists, in the
  2479. !    light of information which has come along since then (i.e., the second
  2480. !    parameter).  It returns a parser error number, or else 0 if all is well.
  2481. !    This only ever throws things out, never adds new ones.
  2482. ! ----------------------------------------------------------------------------
  2483.  
  2484. [ ReviseMulti second_p  i low;
  2485.  
  2486. #ifdef DEBUG;
  2487.   if (parser_trace>=4)
  2488.       print "   Revising multiple object list of size ", multiple_object-->0,
  2489.             " with 2nd ", (name) second_p, "^";
  2490. #endif;
  2491.  
  2492.   if (multi_context==MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN)
  2493.   {   for (i=1, low=0:i<=multiple_object-->0:i++)
  2494.       {   if ( (multi_context==MULTIEXCEPT_TOKEN
  2495.                 && multiple_object-->i ~= second_p)
  2496.                || (multi_context==MULTIINSIDE_TOKEN
  2497.                    && multiple_object-->i in second_p))
  2498.           {   low++; multiple_object-->low = multiple_object-->i;
  2499.           }
  2500.       }
  2501.       multiple_object-->0 = low;
  2502.   }
  2503.  
  2504.   if (multi_context==MULTI_TOKEN && action_to_be == ##Take)
  2505.   {   for (i=1, low=0:i<=multiple_object-->0:i++)
  2506.           if (parent(multiple_object-->i)==parent(actor)) low++;
  2507. #ifdef DEBUG;
  2508.       if (parser_trace>=4)
  2509.           print "   Token 2 plural case: number with actor ", low, "^";
  2510. #endif;
  2511.       if (take_all_rule==2 || low>0)
  2512.       {   for (i=1, low=0:i<=multiple_object-->0:i++)
  2513.           {   if (parent(multiple_object-->i)==parent(actor))
  2514.               {   low++; multiple_object-->low = multiple_object-->i;
  2515.               }
  2516.           }
  2517.           multiple_object-->0 = low;
  2518.       }
  2519.   }
  2520.  
  2521.   i=multiple_object-->0;
  2522. #ifdef DEBUG;
  2523.   if (parser_trace>=4)
  2524.       print "   Done: new size ", i, "^";
  2525. #endif;
  2526.   if (i==0) return NOTHING_PE;
  2527.   return 0;
  2528. ];
  2529.  
  2530. ! ----------------------------------------------------------------------------
  2531. !  ScoreMatchL  scores the match list for quality in terms of what the
  2532. !  player has vaguely asked for.  Points are awarded for conforming with
  2533. !  requirements like "my", and so on.  Remove from the match list any
  2534. !  entries which fail the basic requirements of the descriptors.
  2535. ! ----------------------------------------------------------------------------
  2536.  
  2537. [ ScoreMatchL  its_owner its_score obj i j threshold a_s l_s;
  2538.  
  2539. !  if (indef_type & OTHER_BIT ~= 0) threshold=45;
  2540.   if (indef_type & MY_BIT ~= 0)    threshold=threshold+45;
  2541.   if (indef_type & THAT_BIT ~= 0)  threshold=threshold+45;
  2542.   if (indef_type & LIT_BIT ~= 0)   threshold=threshold+45;
  2543.   if (indef_type & UNLIT_BIT ~= 0) threshold=threshold+45;
  2544.   if (indef_owner ~= nothing)      threshold=threshold+45;
  2545.  
  2546. #ifdef DEBUG;
  2547.   if (parser_trace>=4) print "   Scoring match list: indef mode ", indef_mode,
  2548.       " type ", indef_type,
  2549.       ", threshold ", threshold, ":^";
  2550. #endif;
  2551.  
  2552.   a_s = 30; l_s = 20;
  2553.   if (action_to_be == ##Take or ##Remove) { a_s=20; l_s=30; }
  2554.  
  2555.   for (i=0:i<number_matched:i++)
  2556.   {   obj = match_list-->i; its_owner = parent(obj); its_score=0;
  2557.  
  2558. !      if (indef_type & OTHER_BIT ~=0
  2559. !          &&  obj~=itobj or himobj or herobj)
  2560. !          its_score=its_score+45;
  2561.       if (indef_type & MY_BIT ~=0  &&  its_owner==actor)
  2562.           its_score=its_score+45;
  2563.       if (indef_type & THAT_BIT ~=0  &&  its_owner==actors_location)
  2564.           its_score=its_score+45;
  2565.       if (indef_type & LIT_BIT ~=0  &&  obj has light)
  2566.           its_score=its_score+45;
  2567.       if (indef_type & UNLIT_BIT ~=0  &&  obj hasnt light)
  2568.           its_score=its_score+45;
  2569.       if (indef_owner~=0 && its_owner == indef_owner)
  2570.           its_score=its_score+45;
  2571.  
  2572.       if (its_score < threshold)
  2573.       {
  2574. #ifdef DEBUG;
  2575.           if (parser_trace >= 4)
  2576.               print "   ", (The) match_list-->i,
  2577.                     " (", match_list-->i, ") in ", (the) its_owner,
  2578.                     " is rejected (doesn't match descriptors)^";
  2579. #endif;
  2580.           match_list-->i=-1;
  2581.       }
  2582.       else
  2583.       {   !   Now forget all of that: the object passes descriptors
  2584.           !   and we can score it properly.
  2585.  
  2586.           its_score = 0;
  2587.           if (obj hasnt concealed) its_score=50;
  2588.  
  2589.           if (its_owner==actor)   its_score = its_score+a_s;
  2590.           else
  2591.           if (its_owner==actors_location) its_score = its_score+l_s;
  2592.           else
  2593.           if (its_score==0 && its_owner~=compass) its_score = its_score+10;
  2594.  
  2595.           !   The entry point "ChooseObjects" can contribute 0 to 9 points
  2596.           !   to refine the score
  2597.           its_score = its_score + ChooseObjects(obj,2);
  2598.  
  2599.           !   A small bonus for having the correct GNA,
  2600.           !   for sorting out ambiguous articles and the like.
  2601.  
  2602.           its_score = 2*its_score;
  2603.           if (indef_cases & (PowersOfTwo_TB-->(GetGNAOfObject(obj))))
  2604.               its_score++;
  2605.  
  2606.           match_scores-->i = match_scores-->i + its_score;
  2607. #ifdef DEBUG;
  2608.           if (parser_trace >= 4)
  2609.               print "   ", (The) match_list-->i,
  2610.                     " (", match_list-->i, ") in ", (the) its_owner,
  2611.                     " scores ", match_scores-->i, "^";
  2612. #endif;
  2613.       }
  2614.   }
  2615.  
  2616.   for (i=0:i<number_matched:i++)
  2617.   {   while (match_list-->i == -1)
  2618.       {   if (i == number_matched-1) { number_matched--; break; }
  2619.           for (j=i:j<number_matched:j++)
  2620.               match_list-->j = match_list-->(j+1);
  2621.           number_matched--;
  2622.       }
  2623.   }
  2624. ];
  2625.  
  2626. ! ----------------------------------------------------------------------------
  2627. !  BestGuess makes the best guess it can out of the match list, assuming that
  2628. !  everything in the match list is textually as good as everything else;
  2629. !  however it ignores items marked as -1, and so marks anything it chooses.
  2630. !  It returns -1 if there are no possible choices.
  2631. ! ----------------------------------------------------------------------------
  2632.  
  2633. [ BestGuess  earliest its_score best i;
  2634.  
  2635.   earliest=0; best=-1;
  2636.   for (i=0:i<number_matched:i++)
  2637.   {   if (match_list-->i >= 0)
  2638.       {   its_score=match_scores-->i;
  2639.           if (its_score>best) { best=its_score; earliest=i; }
  2640.       }
  2641.   }
  2642. #ifdef DEBUG;
  2643.   if (parser_trace>=4)
  2644.   {   if (best<0)
  2645.           print "   Best guess ran out of choices^";
  2646.       else
  2647.           print "   Best guess ", (the) match_list-->earliest,
  2648.                 " (", match_list-->earliest, ")^";
  2649.   }
  2650. #endif;
  2651.   if (best<0) return -1;
  2652.   i=match_list-->earliest;
  2653.   match_list-->earliest=-1;
  2654.   return i;
  2655. ];
  2656.  
  2657. ! ----------------------------------------------------------------------------
  2658. !  Identical decides whether or not two objects can be distinguished from
  2659. !  each other by anything the player can type.  If not, it returns true.
  2660. ! ----------------------------------------------------------------------------
  2661.  
  2662. [ Identical o1 o2 p1 p2 n1 n2 i j flag;
  2663.  
  2664.   if (o1==o2) rtrue;  ! This should never happen, but to be on the safe side
  2665.   if (o1==0 || o2==0) rfalse;  ! Similarly
  2666.   if (parent(o1)==compass || parent(o2)==compass) rfalse; ! Saves time
  2667.  
  2668. !  What complicates things is that o1 or o2 might have a parsing routine,
  2669. !  so the parser can't know from here whether they are or aren't the same.
  2670. !  If they have different parsing routines, we simply assume they're
  2671. !  different.  If they have the same routine (which they probably got from
  2672. !  a class definition) then the decision process is as follows:
  2673. !
  2674. !     the routine is called (with self being o1, not that it matters)
  2675. !       with noun and second being set to o1 and o2, and action being set
  2676. !       to the fake action TheSame.  If it returns -1, they are found
  2677. !       identical; if -2, different; and if >=0, then the usual method
  2678. !       is used instead.
  2679.  
  2680.   if (o1.parse_name~=0 || o2.parse_name~=0)
  2681.   {   if (o1.parse_name ~= o2.parse_name) rfalse;
  2682.       parser_action=##TheSame; parser_one=o1; parser_two=o2;
  2683.       j=wn; i=RunRoutines(o1,parse_name); wn=j;
  2684.       if (i==-1) rtrue; if (i==-2) rfalse;
  2685.   }
  2686.  
  2687. !  This is the default algorithm: do they have the same words in their
  2688. !  "name" (i.e. property no. 1) properties.  (Note that the following allows
  2689. !  for repeated words and words in different orders.)
  2690.  
  2691.   p1 = o1.&1; n1 = (o1.#1)/2;
  2692.   p2 = o2.&1; n2 = (o2.#1)/2;
  2693.  
  2694. !  for (i=0:i<n1:i++) { print (address) p1-->i, " "; } new_line;
  2695. !  for (i=0:i<n2:i++) { print (address) p2-->i, " "; } new_line;
  2696.  
  2697.   for (i=0:i<n1:i++)
  2698.   {   flag=0;
  2699.       for (j=0:j<n2:j++)
  2700.           if (p1-->i == p2-->j) flag=1;
  2701.       if (flag==0) rfalse;
  2702.   }
  2703.  
  2704.   for (j=0:j<n2:j++)
  2705.   {   flag=0;
  2706.       for (i=0:i<n1:i++)
  2707.           if (p1-->i == p2-->j) flag=1;
  2708.       if (flag==0) rfalse;
  2709.   }
  2710.  
  2711. !  print "Which are identical!^";
  2712.   rtrue;
  2713. ];
  2714.  
  2715. ! ----------------------------------------------------------------------------
  2716. !  PrintCommand reconstructs the command as it presently reads, from
  2717. !  the pattern which has been built up
  2718. !
  2719. !  If from is 0, it starts with the verb: then it goes through the pattern.
  2720. !  The other parameter is "emptyf" - a flag: if 0, it goes up to pcount:
  2721. !  if 1, it goes up to pcount-1.
  2722. !
  2723. !  Note that verbs and prepositions are printed out of the dictionary:
  2724. !  and that since the dictionary may only preserve the first six characters
  2725. !  of a word (in a V3 game), we have to hand-code the longer words needed.
  2726. !
  2727. !  (Recall that pattern entries are 0 for "multiple object", 1 for "special
  2728. !  word", 2 to REPARSE_CODE-1 are object numbers and REPARSE_CODE+n means the
  2729. !  preposition n)
  2730. ! ----------------------------------------------------------------------------
  2731.  
  2732. [ PrintCommand from i k spacing_flag;
  2733.  
  2734.   if (from==0)
  2735.   {   i=verb_word;
  2736.       if (LanguageVerb(i) == 0)
  2737.           if (PrintVerb(i) == 0)
  2738.               print (address) i;
  2739.       from++; spacing_flag = true;
  2740.   }
  2741.  
  2742.   for (k=from:k<pcount:k++)
  2743.   {   i=pattern-->k;
  2744.       if (i == PATTERN_NULL) continue;
  2745.       if (spacing_flag) print (char) ' ';
  2746.       if (i==0) { print (string) THOSET__TX; jump TokenPrinted; }
  2747.       if (i==1) { print (string) THAT__TX; jump TokenPrinted; }
  2748.       if (i>=REPARSE_CODE) print (address) No__Dword(i-REPARSE_CODE);
  2749.       else print (the) i;
  2750.       .TokenPrinted;
  2751.       spacing_flag = true;
  2752.   }
  2753. ];
  2754.  
  2755. ! ----------------------------------------------------------------------------
  2756. !  The CantSee routine returns a good error number for the situation where
  2757. !  the last word looked at didn't seem to refer to any object in context.
  2758. !
  2759. !  The idea is that: if the actor is in a location (but not inside something
  2760. !  like, for instance, a tank which is in that location) then an attempt to
  2761. !  refer to one of the words listed as meaningful-but-irrelevant there
  2762. !  will cause "you don't need to refer to that in this game" rather than
  2763. !  "no such thing" or "what's 'it'?".
  2764. !  (The advantage of not having looked at "irrelevant" local nouns until now
  2765. !  is that it stops them from clogging up the ambiguity-resolving process.
  2766. !  Thus game objects always triumph over scenery.)
  2767. ! ----------------------------------------------------------------------------
  2768.  
  2769. [ CantSee  i w e;
  2770.     saved_oops=oops_from;
  2771.  
  2772.     if (scope_token~=0) { scope_error = scope_token; return ASKSCOPE_PE; }
  2773.  
  2774.     wn--; w=NextWord();
  2775.     e=CANTSEE_PE;
  2776.     if (w==pronoun_word)
  2777.     {   pronoun__word=pronoun_word; pronoun__obj=pronoun_obj;
  2778.         e=ITGONE_PE;
  2779.     }
  2780.     i=actor; while (parent(i) ~= 0) i = parent(i);
  2781.     if (i has visited && Refers(i,wn-1)==1) e=SCENERY_PE;
  2782.     if (etype>e) return etype;
  2783.     return e;
  2784. ];
  2785.  
  2786. ! ----------------------------------------------------------------------------
  2787. !  The MultiAdd routine adds object "o" to the multiple-object-list.
  2788. !
  2789. !  This is only allowed to hold 63 objects at most, at which point it ignores
  2790. !  any new entries (and sets a global flag so that a warning may later be
  2791. !  printed if need be).
  2792. ! ----------------------------------------------------------------------------
  2793.  
  2794. [ MultiAdd o i j;
  2795.   i=multiple_object-->0;
  2796.   if (i==63) { toomany_flag=1; rtrue; }
  2797.   for (j=1:j<=i:j++)
  2798.       if (o==multiple_object-->j) 
  2799.           rtrue;
  2800.   i++;
  2801.   multiple_object-->i = o;
  2802.   multiple_object-->0 = i;
  2803. ];
  2804.  
  2805. ! ----------------------------------------------------------------------------
  2806. !  The MultiSub routine deletes object "o" from the multiple-object-list.
  2807. !
  2808. !  It returns 0 if the object was there in the first place, and 9 (because
  2809. !  this is the appropriate error number in Parser()) if it wasn't.
  2810. ! ----------------------------------------------------------------------------
  2811.  
  2812. [ MultiSub o i j k et;
  2813.   i=multiple_object-->0; et=0;
  2814.   for (j=1:j<=i:j++)
  2815.       if (o==multiple_object-->j)
  2816.       {   for (k=j:k<=i:k++)
  2817.               multiple_object-->k = multiple_object-->(k+1);
  2818.           multiple_object-->0 = --i;
  2819.           return et;
  2820.       }
  2821.   et=9; return et;
  2822. ];
  2823.  
  2824. ! ----------------------------------------------------------------------------
  2825. !  The MultiFilter routine goes through the multiple-object-list and throws
  2826. !  out anything without the given attribute "attr" set.
  2827. ! ----------------------------------------------------------------------------
  2828.  
  2829. [ MultiFilter attr  i j o;
  2830.   .MFiltl;
  2831.   i=multiple_object-->0;
  2832.   for (j=1:j<=i:j++)
  2833.   {   o=multiple_object-->j;
  2834.       if (o hasnt attr) { MultiSub(o); jump Mfiltl; }
  2835.   }
  2836. ];
  2837.  
  2838. ! ----------------------------------------------------------------------------
  2839. !  The UserFilter routine consults the user's filter (or checks on attribute)
  2840. !  to see what already-accepted nouns are acceptable
  2841. ! ----------------------------------------------------------------------------
  2842.  
  2843. [ UserFilter obj;
  2844.  
  2845.   if (token_filter > 0 && token_filter < 49)
  2846.   {   if (obj has (token_filter-1)) rtrue;
  2847.       rfalse;
  2848.   }
  2849.   noun = obj;
  2850.   return indirect(token_filter);
  2851. ];
  2852.  
  2853. ! ----------------------------------------------------------------------------
  2854. !  MoveWord copies word at2 from parse buffer b2 to word at1 in "parse"
  2855. !  (the main parse buffer)
  2856. ! ----------------------------------------------------------------------------
  2857.  
  2858. [ MoveWord at1 b2 at2 x y;
  2859.   x=at1*2-1; y=at2*2-1;
  2860.   parse-->x++ = b2-->y++;
  2861.   parse-->x = b2-->y;
  2862. ];
  2863.  
  2864. ! ----------------------------------------------------------------------------
  2865. !  SearchScope  domain1 domain2 context
  2866. !
  2867. !  Works out what objects are in scope (possibly asking an outside routine),
  2868. !  but does not look at anything the player has typed.
  2869. ! ----------------------------------------------------------------------------
  2870.  
  2871. [ SearchScope domain1 domain2 context i;
  2872.  
  2873.   i=0;
  2874. !  Everything is in scope to the debugging commands
  2875.  
  2876. #ifdef DEBUG;
  2877.   if (scope_reason==PARSING_REASON
  2878.       && verb_word == 'purloin' or 'tree' or 'abstract'
  2879.                        or 'gonear' or 'scope' or 'showobj')
  2880.   {   for (i=selfobj:i<=top_object:i++) PlaceInScope(i);
  2881.       rtrue;
  2882.   }
  2883. #endif;
  2884.  
  2885. !  First, a scope token gets priority here:
  2886.  
  2887.   if (scope_token ~= 0)
  2888.   {   scope_stage=2;
  2889.       if (indirect(scope_token)~=0) rtrue;
  2890.   }
  2891.  
  2892. !  Next, call any user-supplied routine adding things to the scope,
  2893. !  which may circumvent the usual routines altogether if they return true:
  2894.  
  2895.   if (actor==domain1 or domain2 && InScope(actor)~=0) rtrue;
  2896.  
  2897. !  Pick up everything in the location except the actor's possessions;
  2898. !  then go through those.  (This ensures the actor's possessions are in
  2899. !  scope even in Darkness.)
  2900.  
  2901.   if (context==MULTIINSIDE_TOKEN && advance_warning ~= -1)
  2902.   {   if (IsSeeThrough(advance_warning)==1)
  2903.           ScopeWithin(advance_warning, 0, context);
  2904.   }
  2905.   else
  2906.   {   if (domain1~=0 && domain1 has supporter or container)
  2907.           ScopeWithin_O(domain1, domain1, context);
  2908.       ScopeWithin(domain1, domain2, context);
  2909.       if (domain2~=0 && domain2 has supporter or container)
  2910.           ScopeWithin_O(domain2, domain2, context);
  2911.       ScopeWithin(domain2, 0, context);
  2912.   }
  2913.  
  2914. !  A special rule applies:
  2915. !  in Darkness as in light, the actor is always in scope to himself.
  2916.  
  2917.   if (thedark == domain1 or domain2)
  2918.   {   ScopeWithin_O(actor, actor, context);
  2919.       if (parent(actor) has supporter or container)
  2920.           ScopeWithin_O(parent(actor), parent(actor), context);
  2921.   }
  2922. ];
  2923.  
  2924. ! ----------------------------------------------------------------------------
  2925. !  IsSeeThrough is used at various places: roughly speaking, it determines
  2926. !  whether o being in scope means that the contents of o are in scope.
  2927. ! ----------------------------------------------------------------------------
  2928.  
  2929. [ IsSeeThrough o;
  2930.   if (o has supporter
  2931.       || (o has transparent)
  2932.       || (o has container && o has open))
  2933.       rtrue;
  2934.   rfalse;
  2935. ];
  2936.  
  2937. ! ----------------------------------------------------------------------------
  2938. !  PlaceInScope is provided for routines outside the library, and is not
  2939. !  called within the parser (except for debugging purposes).
  2940. ! ----------------------------------------------------------------------------
  2941.  
  2942. [ PlaceInScope thing;
  2943.    if (scope_reason~=PARSING_REASON or TALKING_REASON)
  2944.    {   DoScopeAction(thing); rtrue; }
  2945.    wn=match_from; TryGivenObject(thing); placed_in_flag=1;
  2946. ];
  2947.  
  2948. ! ----------------------------------------------------------------------------
  2949. !  DoScopeAction
  2950. ! ----------------------------------------------------------------------------
  2951.  
  2952. [ DoScopeAction thing s p1;
  2953.   s = scope_reason; p1=parser_one;
  2954. #ifdef DEBUG;
  2955.   if (parser_trace>=6)
  2956.   {   print "[DSA on ", (the) thing, " with reason = ", scope_reason,
  2957.       " p1 = ", parser_one, " p2 = ", parser_two, "]^";
  2958.   }
  2959. #endif;
  2960.   switch(scope_reason)
  2961.   {   REACT_BEFORE_REASON:
  2962.           if (thing.react_before==0 or NULL) return;
  2963. #ifdef DEBUG;
  2964.           if (parser_trace>=2)
  2965.           {   print "[Considering react_before for ", (the) thing, "]^"; }
  2966. #endif;
  2967.           if (parser_one==0) parser_one = RunRoutines(thing,react_before);
  2968.       REACT_AFTER_REASON:
  2969.           if (thing.react_after==0 or NULL) return;
  2970. #ifdef DEBUG;
  2971.           if (parser_trace>=2)
  2972.           {   print "[Considering react_after for ", (the) thing, "]^"; }
  2973. #endif;
  2974.           if (parser_one==0) parser_one = RunRoutines(thing,react_after);
  2975.       EACH_TURN_REASON:
  2976.           if (thing.&each_turn==0) return;
  2977. #ifdef DEBUG;
  2978.           if (parser_trace>=2)
  2979.           {   print "[Considering each_turn for ", (the) thing, "]^"; }
  2980. #endif;
  2981.           PrintOrRun(thing, each_turn);
  2982.       TESTSCOPE_REASON:
  2983.           if (thing==parser_one) parser_two = 1;
  2984.       LOOPOVERSCOPE_REASON:
  2985.           indirect(parser_one,thing); parser_one=p1;
  2986.   }
  2987.   scope_reason = s;
  2988. ];
  2989.  
  2990. ! ----------------------------------------------------------------------------
  2991. !  ScopeWithin looks for objects in the domain which make textual sense
  2992. !  and puts them in the match list.  (However, it does not recurse through
  2993. !  the second argument.)
  2994. ! ----------------------------------------------------------------------------
  2995.  
  2996. [ ScopeWithin domain nosearch context;
  2997.  
  2998.    if (domain==0) rtrue;
  2999.  
  3000. !  Special rule: the directions (interpreted as the 12 walls of a room) are
  3001. !  always in context.  (So, e.g., "examine north wall" is always legal.)
  3002. !  (Unless we're parsing something like "all", because it would just slow
  3003. !  things down then, or unless the context is "creature".)
  3004.  
  3005.    if (indef_mode==0 && domain==actors_location
  3006.        && scope_reason==PARSING_REASON && context~=CREATURE_TOKEN)
  3007.            ScopeWithin(compass);
  3008.  
  3009. !  Look through the objects in the domain
  3010.    objectloop (domain in domain) ScopeWithin_O(domain, nosearch, context);
  3011. ];
  3012.  
  3013. [ ScopeWithin_O domain nosearch context i ad n;
  3014.  
  3015. !  multiexcept doesn't have second parameter in scope
  3016.    if (context==MULTIEXCEPT_TOKEN && domain==advance_warning) jump DontAccept;
  3017.  
  3018. !  If the scope reason is unusual, don't parse.
  3019.  
  3020.       if (scope_reason~=PARSING_REASON or TALKING_REASON)
  3021.       {   DoScopeAction(domain); jump DontAccept; }
  3022.  
  3023. !  "it" or "them" matches to the it-object only.  (Note that (1) this means
  3024. !  that "it" will only be understood if the object in question is still
  3025. !  in context, and (2) only one match can ever be made in this case.)
  3026.  
  3027.       if (match_from <= num_words)  ! If there's any text to match, that is
  3028.       {   wn=match_from;
  3029.           i=NounWord();
  3030.           if (i==1 && player==domain)  MakeMatch(domain, 1);
  3031.  
  3032.           if (i>=2 && i<128 && (LanguagePronouns-->i == domain))
  3033.               MakeMatch(domain, 1);
  3034.       }
  3035.  
  3036. !  Construing the current word as the start of a noun, can it refer to the
  3037. !  object?
  3038.  
  3039.       wn = match_from;
  3040.       if (TryGivenObject(domain) > 0)
  3041.           if (indef_nspec_at>0 && match_from~=indef_nspec_at)
  3042.           {   !  This case arises if the player has typed a number in
  3043.               !  which is hypothetically an indefinite descriptor:
  3044.               !  e.g. "take two clubs".  We have just checked the object
  3045.               !  against the word "clubs", in the hope of eventually finding
  3046.               !  two such objects.  But we also backtrack and check it
  3047.               !  against the words "two clubs", in case it turns out to
  3048.               !  be the 2 of Clubs from a pack of cards, say.  If it does
  3049.               !  match against "two clubs", we tear up our original
  3050.               !  assumption about the meaning of "two" and lapse back into
  3051.               !  definite mode.
  3052.           
  3053.               wn = indef_nspec_at;
  3054.               if (TryGivenObject(domain) > 0)
  3055.               {   match_from = indef_nspec_at;
  3056.                   ResetDescriptors();                  
  3057.               }
  3058.               wn = match_from;
  3059.           }
  3060.  
  3061.       .DontAccept;
  3062.  
  3063. !  Shall we consider the possessions of the current object, as well?
  3064. !  Only if it's a container (so, for instance, if a dwarf carries a
  3065. !  sword, then "drop sword" will not be accepted, but "dwarf, drop sword"
  3066. !  will).
  3067. !  Also, only if there are such possessions.
  3068. !
  3069. !  Notice that the parser can see "into" anything flagged as
  3070. !  transparent - such as a dwarf whose sword you can get at.
  3071.  
  3072.       if (child(domain)~=0 && domain ~= nosearch && IsSeeThrough(domain)==1)
  3073.           ScopeWithin(domain,nosearch,context);
  3074.  
  3075. !  Drag any extras into context
  3076.  
  3077.    ad = domain.&add_to_scope;
  3078.    if (ad ~= 0)
  3079.    {   if (UnsignedCompare(ad-->0,top_object) > 0)
  3080.        {   ats_flag = 2+context;
  3081.            RunRoutines(domain, add_to_scope);
  3082.            ats_flag = 0;
  3083.        }
  3084.        else
  3085.        {   n=domain.#add_to_scope;
  3086.            for (i=0:(2*i)<n:i++)
  3087.                ScopeWithin_O(ad-->i,0,context);
  3088.        }
  3089.    }
  3090. ];
  3091.  
  3092. [ AddToScope obj;
  3093.    if (ats_flag>=2)
  3094.        ScopeWithin_O(obj,0,ats_flag-2);
  3095.    if (ats_flag==1)
  3096.    {   if  (HasLightSource(obj)==1) ats_hls = 1;
  3097.    }
  3098. ];
  3099.  
  3100. ! ----------------------------------------------------------------------------
  3101. !  MakeMatch looks at how good a match is.  If it's the best so far, then
  3102. !  wipe out all the previous matches and start a new list with this one.
  3103. !  If it's only as good as the best so far, add it to the list.
  3104. !  If it's worse, ignore it altogether.
  3105. !
  3106. !  The idea is that "red panic button" is better than "red button" or "panic".
  3107. !
  3108. !  number_matched (the number of words matched) is set to the current level
  3109. !  of quality.
  3110. !
  3111. !  We never match anything twice, and keep at most 64 equally good items.
  3112. ! ----------------------------------------------------------------------------
  3113.  
  3114. [ MakeMatch obj quality i;
  3115. #ifdef DEBUG;
  3116.    if (parser_trace>=6) print "    Match with quality ",quality,"^";
  3117. #endif;
  3118.    if (token_filter~=0 && UserFilter(obj)==0)
  3119.    {   #ifdef DEBUG;
  3120.        if (parser_trace>=6)
  3121.        {   print "    Match filtered out: token filter ", token_filter, "^";
  3122.        }
  3123.        #endif;
  3124.        rtrue;
  3125.    }
  3126.    if (quality < match_length) rtrue;
  3127.    if (quality > match_length) { match_length=quality; number_matched=0; }
  3128.    else
  3129.    {   if (2*number_matched>=MATCH_LIST_SIZE) rtrue;
  3130.        for (i=0:i<number_matched:i++)
  3131.            if (match_list-->i==obj) rtrue;
  3132.    }
  3133.    match_list-->number_matched++ = obj;
  3134. #ifdef DEBUG;
  3135.    if (parser_trace>=6) print "    Match added to list^";
  3136. #endif;
  3137. ];
  3138.  
  3139. ! ----------------------------------------------------------------------------
  3140. !  TryGivenObject tries to match as many words as possible in what has been
  3141. !  typed to the given object, obj.  If it manages any words matched at all,
  3142. !  it calls MakeMatch to say so, then returns the number of words (or 1
  3143. !  if it was a match because of inadequate input).
  3144. ! ----------------------------------------------------------------------------
  3145.  
  3146. [ TryGivenObject obj threshold k w j;
  3147.  
  3148. #ifdef DEBUG;
  3149.    if (parser_trace>=5)
  3150.        print "    Trying ", (the) obj, " (", obj, ") at word ", wn, "^";
  3151. #endif;
  3152.  
  3153.    dict_flags_of_noun = 0;
  3154.  
  3155. !  If input has run out then always match, with only quality 0 (this saves
  3156. !  time).
  3157.  
  3158.    if (wn > num_words)
  3159.    {   if (indef_mode ~= 0)
  3160.            dict_flags_of_noun = $$01110000;  ! Reject "plural" bit
  3161.        MakeMatch(obj,0);
  3162.        #ifdef DEBUG;
  3163.        if (parser_trace>=5)
  3164.        print "    Matched (0)^";
  3165.        #endif;
  3166.        return 1;
  3167.    }
  3168.  
  3169. !  Ask the object to parse itself if necessary, sitting up and taking notice
  3170. !  if it says the plural was used:
  3171.  
  3172.    if (obj.parse_name~=0)
  3173.    {   parser_action = NULL; j=wn;
  3174.        k=RunRoutines(obj,parse_name);
  3175.        if (k>0)
  3176.        {   wn=j+k;
  3177.            .MMbyPN;
  3178.  
  3179.            if (parser_action == ##PluralFound)
  3180.                dict_flags_of_noun = dict_flags_of_noun | 4;
  3181.  
  3182.            if (dict_flags_of_noun & 4)
  3183.            {   if (~~allow_plurals) k=0;
  3184.                else
  3185.                {   if (indef_mode==0)
  3186.                    {   indef_mode=1; indef_type=0; indef_wanted=0; }
  3187.                    indef_type = indef_type | PLURAL_BIT;
  3188.                    if (indef_wanted==0) indef_wanted=100;
  3189.                }
  3190.            }
  3191.  
  3192.            #ifdef DEBUG;
  3193.                if (parser_trace>=5)
  3194.                {   print "    Matched (", k, ")^";
  3195.                }
  3196.            #endif;
  3197.            MakeMatch(obj,k);
  3198.            return k;
  3199.        }
  3200.        if (k==0) jump NoWordsMatch;
  3201.    }
  3202.  
  3203. !  The default algorithm is simply to count up how many words pass the
  3204. !  Refers test:
  3205.  
  3206.    parser_action = NULL;
  3207.  
  3208.    w = NounWord();
  3209.  
  3210.    if (w==1 && player==obj) { k=1; jump MMbyPN; }
  3211.  
  3212.    if (w>=2 && w<128 && (LanguagePronouns-->w == obj))
  3213.    {   k=1; jump MMbyPN; }
  3214.  
  3215.    j=--wn;
  3216.    threshold = ParseNoun(obj);
  3217. #ifdef DEBUG;
  3218.    if (threshold>=0 && parser_trace>=5)
  3219.        print "    ParseNoun returned ", threshold, "^";
  3220. #endif;
  3221.    if (threshold<0) wn++;
  3222.    if (threshold>0) { k=threshold; jump MMbyPN; }
  3223.  
  3224.    if (threshold==0 || Refers(obj,wn-1)==0)
  3225.    {   .NoWordsMatch;
  3226.        if (indef_mode~=0)
  3227.        {   k=0; parser_action=NULL; jump MMbyPN;
  3228.        }
  3229.        rfalse;
  3230.    }
  3231.  
  3232.    if (threshold<0)
  3233.    {   threshold=1;
  3234.        dict_flags_of_noun = (w->#dict_par1) & $$01110100;
  3235.        w = NextWord();
  3236.        while (Refers(obj, wn-1))
  3237.        {   threshold++;
  3238.            if (w)
  3239.                dict_flags_of_noun = dict_flags_of_noun
  3240.                                     | ((w->#dict_par1) & $$01110100);
  3241.            w = NextWord();
  3242.        }
  3243.    }
  3244.  
  3245.    k = threshold; jump MMbyPN;
  3246. ];
  3247.  
  3248. ! ----------------------------------------------------------------------------
  3249. !  Refers works out whether the word at number wnum can refer to the object
  3250. !  obj, returning true or false.  The standard method is to see if the
  3251. !  word is listed under "name" for the object, but this is more complex
  3252. !  in languages other than English.
  3253. ! ----------------------------------------------------------------------------
  3254.  
  3255. [ Refers obj wnum   wd k l m;
  3256.     if (obj==0) rfalse;
  3257.  
  3258.     #ifdef LanguageRefers;
  3259.     k = LanguageRefers(obj,wnum); if (k>=0) return k;
  3260.     #endif;
  3261.  
  3262.     k = wn; wn = wnum; wd = NextWordStopped(); wn = k;
  3263.  
  3264.     if (parser_inflection >= 256)
  3265.     {   k = indirect(parser_inflection, obj, wd);
  3266.         if (k>=0) return k;
  3267.         m = -k;
  3268.     } else m = parser_inflection;
  3269.     k=obj.&m; l=(obj.#m)/2-1;
  3270.     for (m=0:m<=l:m++)
  3271.         if (wd==k-->m) rtrue;
  3272.     rfalse;
  3273. ];
  3274.  
  3275. [ WordInProperty wd obj prop k l m;
  3276.     k=obj.∝ l=(obj.#prop)/2-1;
  3277.     for (m=0:m<=l:m++)
  3278.         if (wd==k-->m) rtrue;
  3279.     rfalse;
  3280. ];
  3281.  
  3282. [ DictionaryLookup b l i;
  3283.   for (i=0:i<l:i++) buffer2->(2+i) = b->i;
  3284.   buffer2->1 = l;
  3285.   Tokenise__(buffer2,parse2);
  3286.   return parse2-->1;
  3287. ];
  3288.  
  3289. ! ----------------------------------------------------------------------------
  3290. !  NounWord (which takes no arguments) returns:
  3291. !
  3292. !   0  if the next word is unrecognised or does not carry the "noun" bit in
  3293. !      its dictionary entry,
  3294. !   1  if a word meaning "me",
  3295. !   the index in the pronoun table (plus 2) of the value field of a pronoun,
  3296. !      if the word is a pronoun,
  3297. !   the address in the dictionary if it is a recognised noun.
  3298. !
  3299. !  The "current word" marker moves on one.
  3300. ! ----------------------------------------------------------------------------
  3301.  
  3302. [ NounWord i j s;
  3303.    i=NextWord();
  3304.    if (i==0) rfalse;
  3305.    if (i==ME1__WD or ME2__WD or ME3__WD) return 1;
  3306.    s = LanguagePronouns-->0;
  3307.    for (j=1 : j<=s : j=j+3)
  3308.        if (i == LanguagePronouns-->j)
  3309.            return j+2;
  3310.    if ((i->#dict_par1)&128 == 0) rfalse;
  3311.    return i;
  3312. ];
  3313.  
  3314. ! ----------------------------------------------------------------------------
  3315. !  NextWord (which takes no arguments) returns:
  3316. !
  3317. !  0            if the next word is unrecognised,
  3318. !  comma_word   if it is a comma character
  3319. !               (which is treated oddly by the Z-machine, hence the code)
  3320. !  or the dictionary address if it is recognised.
  3321. !  The "current word" marker is moved on.
  3322. !
  3323. !  NextWordStopped does the same, but returns -1 when input has run out
  3324. ! ----------------------------------------------------------------------------
  3325.  
  3326. [ NextWord i j k;
  3327.    if (wn > parse->1) { wn++; rfalse; }
  3328.    i=wn*2-1; wn++;
  3329.    j=parse-->i;
  3330.    if (j==0)
  3331.    {   k=wn*4-3; i=buffer->(parse->k);
  3332.        if (i==',') j=comma_word;
  3333.        if (i=='.') j=THEN1__WD;
  3334.    }
  3335.    return j;
  3336. ];   
  3337.  
  3338. [ NextWordStopped;
  3339.    if (wn > parse->1) { wn++; return -1; }
  3340.    return NextWord();
  3341. ];
  3342.  
  3343. [ WordAddress wordnum;
  3344.    return buffer + parse->(wordnum*4+1);
  3345. ];
  3346.  
  3347. [ WordLength wordnum;
  3348.    return parse->(wordnum*4);
  3349. ];
  3350.  
  3351. ! ----------------------------------------------------------------------------
  3352. !  TryNumber is the only routine which really does any character-level
  3353. !  parsing, since that's normally left to the Z-machine.
  3354. !  It takes word number "wordnum" and tries to parse it as an (unsigned)
  3355. !  decimal number, returning
  3356. !
  3357. !  -1000                if it is not a number
  3358. !  the number           if it has between 1 and 4 digits
  3359. !  10000                if it has 5 or more digits.
  3360. !
  3361. !  (The danger of allowing 5 digits is that Z-machine integers are only
  3362. !  16 bits long, and anyway this isn't meant to be perfect.)
  3363. !
  3364. !  Using NumberWord, it also catches "one" up to "twenty".
  3365. !
  3366. !  Note that a game can provide a ParseNumber routine which takes priority,
  3367. !  to enable parsing of odder numbers ("x45y12", say).
  3368. ! ----------------------------------------------------------------------------
  3369.  
  3370. [ TryNumber wordnum   i j c num len mul tot d digit;
  3371.  
  3372.    i=wn; wn=wordnum; j=NextWord(); wn=i;
  3373.    j=NumberWord(j); if (j>=1) return j;
  3374.  
  3375.    i=wordnum*4+1; j=parse->i; num=j+buffer; len=parse->(i-1);
  3376.  
  3377.    tot=ParseNumber(num, len);  if (tot~=0) return tot;
  3378.  
  3379.    if (len>=4) mul=1000;
  3380.    if (len==3) mul=100;
  3381.    if (len==2) mul=10;
  3382.    if (len==1) mul=1;
  3383.  
  3384.    tot=0; c=0; len=len-1;
  3385.  
  3386.    for (c=0:c<=len:c++)
  3387.    {   digit=num->c;
  3388.        if (digit=='0') { d=0; jump digok; }
  3389.        if (digit=='1') { d=1; jump digok; }
  3390.        if (digit=='2') { d=2; jump digok; }
  3391.        if (digit=='3') { d=3; jump digok; }
  3392.        if (digit=='4') { d=4; jump digok; }
  3393.        if (digit=='5') { d=5; jump digok; }
  3394.        if (digit=='6') { d=6; jump digok; }
  3395.        if (digit=='7') { d=7; jump digok; }
  3396.        if (digit=='8') { d=8; jump digok; }
  3397.        if (digit=='9') { d=9; jump digok; }
  3398.        return -1000;
  3399.      .digok;
  3400.        tot=tot+mul*d; mul=mul/10;
  3401.    }
  3402.    if (len>3) tot=10000;
  3403.    return tot;
  3404. ];
  3405.  
  3406. ! ----------------------------------------------------------------------------
  3407. !  GetGender returns 0 if the given animate object is female, and 1 if male
  3408. !  (not all games will want such a simple decision function!)
  3409. ! ----------------------------------------------------------------------------
  3410.  
  3411. [ GetGender person;
  3412.    if (person hasnt female) rtrue;
  3413.    rfalse;
  3414. ];
  3415.  
  3416. [ GetGNAOfObject obj case gender;
  3417.    if (obj hasnt animate) case = 6;
  3418.    if (obj has male) gender = male;
  3419.    if (obj has female) gender = female;
  3420.    if (obj has neuter) gender = neuter;
  3421.    if (gender == 0)
  3422.    {   if (case == 0) gender = LanguageAnimateGender;
  3423.        else gender = LanguageInanimateGender;
  3424.    }
  3425.    if (gender == female) case = case + 1;
  3426.    if (gender == neuter) case = case + 2;
  3427.    if (obj has pluralname) case = case + 3;
  3428.    return case;
  3429. ];
  3430.  
  3431. ! ----------------------------------------------------------------------------
  3432. !  Converting between dictionary addresses and entry numbers
  3433. ! ----------------------------------------------------------------------------
  3434.  
  3435. [ Dword__No w; return (w-(0-->4 + 7))/9; ];
  3436. [ No__Dword n; return 0-->4 + 7 + 9*n; ];
  3437.  
  3438. ! ----------------------------------------------------------------------------
  3439. !  For copying buffers
  3440. ! ----------------------------------------------------------------------------
  3441.  
  3442. [ CopyBuffer bto bfrom i size;
  3443.    size=bto->0;
  3444.    for (i=1:i<=size:i++) bto->i=bfrom->i;
  3445. ];
  3446.  
  3447. ! ----------------------------------------------------------------------------
  3448. !  Provided for use by language definition files
  3449. ! ----------------------------------------------------------------------------
  3450.  
  3451. [ LTI_Insert i ch  y;
  3452.  
  3453.   !   Insert character ch into buffer at point i.
  3454.  
  3455.   !   Being careful not to let the buffer possibly overflow:
  3456.  
  3457.       y = buffer->1;
  3458.       if (y > buffer->0) y = buffer->0;
  3459.  
  3460.   !   Move the subsequent text along one character:
  3461.  
  3462.       for (y=y+2: y>i : y--) buffer->y = buffer->(y-1);
  3463.  
  3464.       buffer->i = ch;
  3465.  
  3466.   !   And the text is now one character longer:
  3467.       if (buffer->1 < buffer->0)
  3468.           (buffer->1)++;
  3469. ];
  3470.  
  3471. ! ============================================================================
  3472.  
  3473. [ PronounsSub x y c d;
  3474.  
  3475.   L__M(##Pronouns, 1);
  3476.  
  3477.   c = (LanguagePronouns-->0)/3;
  3478.   if (player ~= selfobj) c++;
  3479.  
  3480.   if (c==0) return L__M(##Pronouns, 4);
  3481.  
  3482.   for (x = 1, d = 0 : x <= LanguagePronouns-->0: x = x+3)
  3483.   {   print "~", (address) LanguagePronouns-->x, "~ ";
  3484.       y = LanguagePronouns-->(x+2);
  3485.       if (y == NULL) L__M(##Pronouns, 3);
  3486.       else { L__M(##Pronouns, 2); print (the) y; }
  3487.       d++;
  3488.       if (d < c-1) print ", ";
  3489.       if (d == c-1) print (string) AND__TX;
  3490.   }
  3491.   if (player ~= selfobj)
  3492.   {   print "~", (address) ME1__WD, "~ "; L__M(##Pronouns, 2);
  3493.       c = player; player = selfobj;
  3494.       print (the) c; player = c;
  3495.   }
  3496.   ".";
  3497. ];
  3498.  
  3499. [ SetPronoun dword value x;
  3500.   for (x = 1 : x <= LanguagePronouns-->0: x = x+3)
  3501.       if (LanguagePronouns-->x == dword)
  3502.       {   LanguagePronouns-->(x+2) = value; return;
  3503.       }
  3504.   RunTimeError(14);
  3505. ];
  3506.  
  3507. [ PronounValue dword x;
  3508.   for (x = 1 : x <= LanguagePronouns-->0: x = x+3)
  3509.       if (LanguagePronouns-->x == dword)
  3510.           return LanguagePronouns-->(x+2);
  3511.   return 0;
  3512. ];
  3513.  
  3514. [ ResetVagueWords obj; PronounNotice(obj); ];
  3515.  
  3516. #ifdef EnglishNaturalLanguage;
  3517. [ PronounOldEnglish;
  3518.    if (itobj ~= old_itobj)   SetPronoun('it', itobj);
  3519.    if (himobj ~= old_himobj) SetPronoun('him', himobj);
  3520.    if (herobj ~= old_herobj) SetPronoun('her', herobj);
  3521.    old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
  3522. ];
  3523. #endif;
  3524.  
  3525. [ PronounNotice obj x bm;
  3526.  
  3527.    if (obj == player) return;
  3528.  
  3529.    #ifdef EnglishNaturalLanguage;
  3530.    PronounOldEnglish();
  3531.    #endif;
  3532.  
  3533.    bm = PowersOfTwo_TB-->(GetGNAOfObject(obj));
  3534.  
  3535.    for (x = 1 : x <= LanguagePronouns-->0: x = x+3)
  3536.        if (bm & (LanguagePronouns-->(x+1)) ~= 0)
  3537.            LanguagePronouns-->(x+2) = obj;
  3538.  
  3539.    #ifdef EnglishNaturalLanguage;
  3540.    itobj  = PronounValue('it');  old_itobj  = itobj;
  3541.    himobj = PronounValue('him'); old_himobj = himobj;
  3542.    herobj = PronounValue('her'); old_herobj = herobj;
  3543.    #endif;
  3544. ];
  3545.  
  3546. ! ============================================================================
  3547. !  End of the parser proper: the remaining routines are its front end.
  3548. ! ----------------------------------------------------------------------------
  3549.  
  3550. Object InformLibrary "(Inform Library)"
  3551.   with play
  3552.        [ i j k l;
  3553.        standard_interpreter = $32-->0;
  3554.        transcript_mode = ((0-->8) & 1);
  3555.        ChangeDefault(cant_go, CANTGO__TX);
  3556.        
  3557.        real_location = thedark;
  3558.        player = selfobj;
  3559.     
  3560.        top_object = #largest_object-255;
  3561.        selfobj.capacity = MAX_CARRIED;
  3562.        #ifdef LanguageInitialise;
  3563.        LanguageInitialise();
  3564.        #endif;
  3565.        j=Initialise();
  3566.        last_score = score;
  3567.        move player to location;
  3568.        while (parent(location)~=0) location=parent(location);
  3569.        objectloop (i in player) give i moved ~concealed;
  3570.     
  3571.        if (j~=2) Banner();
  3572.  
  3573.        MoveFloatingObjects();
  3574.        lightflag=OffersLight(parent(player));
  3575.        if (lightflag==0) { real_location=location; location=thedark; }
  3576.        <Look>;
  3577.     
  3578.        for (i=1:i<=100:i++) j=random(i);
  3579.  
  3580.        #ifdef EnglishNaturalLanguage;
  3581.        old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
  3582.        #endif;
  3583.     
  3584.        while (~~deadflag)
  3585.        {   
  3586.            #ifdef EnglishNaturalLanguage;
  3587.                PronounOldEnglish();
  3588.                old_itobj = PronounValue('it');
  3589.                old_himobj = PronounValue('him');
  3590.                old_herobj = PronounValue('her');
  3591.            #endif;
  3592.  
  3593.            .very__late__error;
  3594.  
  3595.            if (score ~= last_score)
  3596.            {   if (notify_mode==1) NotifyTheScore(); last_score=score; }
  3597.  
  3598.            .late__error;
  3599.  
  3600.            inputobjs-->0 = 0; inputobjs-->1 = 0;
  3601.            inputobjs-->2 = 0; inputobjs-->3 = 0; meta=false;
  3602.     
  3603.            !  The Parser writes its results into inputobjs and meta,
  3604.            !  a flag indicating a "meta-verb".  This can only be set for
  3605.            !  commands by the player, not for orders to others.
  3606.     
  3607.            InformParser.parse_input(inputobjs);
  3608.     
  3609.            action=inputobjs-->0;
  3610.  
  3611.            !  --------------------------------------------------------------
  3612.  
  3613.            !  Reverse "give fred biscuit" into "give biscuit to fred"
  3614.     
  3615.            if (action==##GiveR or ##ShowR)
  3616.            {   i=inputobjs-->2; inputobjs-->2=inputobjs-->3; inputobjs-->3=i;
  3617.                if (action==##GiveR) action=##Give; else action=##Show;
  3618.            }
  3619.     
  3620.            !  Convert "P, tell me about X" to "ask P about X"
  3621.     
  3622.            if (action==##Tell && inputobjs-->2==player && actor~=player)
  3623.            {   inputobjs-->2=actor; actor=player; action=##Ask;
  3624.            }
  3625.     
  3626.            !  Convert "ask P for X" to "P, give X to me"
  3627.     
  3628.            if (action==##AskFor && inputobjs-->2~=player && actor==player)
  3629.            {   actor=inputobjs-->2; inputobjs-->2=inputobjs-->3;
  3630.                inputobjs-->3=player; action=##Give;
  3631.            }
  3632.     
  3633.            !  For old, obsolete code: special_word contains the topic word
  3634.            !  in conversation
  3635.     
  3636.            if (action==##Ask or ##Tell or ##Answer)
  3637.                special_word = special_number1;
  3638.  
  3639.            !  --------------------------------------------------------------
  3640.     
  3641.            multiflag=false; onotheld_mode=notheld_mode; notheld_mode=false;
  3642.            !  For implicit taking and multiple object detection
  3643.     
  3644.           .begin__action;
  3645.            inp1 = 0; inp2 = 0; i=inputobjs-->1;
  3646.            if (i>=1) inp1=inputobjs-->2;
  3647.            if (i>=2) inp2=inputobjs-->3;
  3648.     
  3649.            !  inp1 and inp2 hold: object numbers, or 0 for "multiple object",
  3650.            !  or 1 for "a number or dictionary address"
  3651.     
  3652.            if (inp1 == 1) noun = special_number1; else noun = inp1;
  3653.            if (inp2 == 1)
  3654.            {   if (inp1 == 1) second = special_number2;
  3655.                else second = special_number1;
  3656.            } else second = inp2;
  3657.  
  3658.            !  --------------------------------------------------------------
  3659.     
  3660.            if (actor~=player)
  3661.            {   
  3662.            !  The player's "orders" property can refuse to allow conversation
  3663.            !  here, by returning true.  If not, the order is sent to the
  3664.            !  other person's "orders" property.  If that also returns false,
  3665.            !  then: if it was a misunderstood command anyway, it is converted
  3666.            !  to an Answer action (thus "floyd, grrr" ends up as
  3667.            !  "say grrr to floyd").  If it was a good command, it is finally
  3668.            !  offered to the Order: part of the other person's "life"
  3669.            !  property, the old-fashioned way of dealing with conversation.
  3670.     
  3671.                j=RunRoutines(player,orders);
  3672.                if (j==0)
  3673.                {   j=RunRoutines(actor,orders);
  3674.                    if (j==0)
  3675.                    {   if (action==##NotUnderstood)
  3676.                        {   inputobjs-->3=actor; actor=player; action=##Answer;
  3677.                            jump begin__action;
  3678.                        }
  3679.                        if (RunLife(actor,##Order)==0) L__M(##Order,1,actor);
  3680.                    }
  3681.                }
  3682.                jump turn__end;
  3683.            }
  3684.  
  3685.            !  --------------------------------------------------------------
  3686.            !  Generate the action...
  3687.  
  3688.            if ((i==0)
  3689.                || (i==1 && inp1 ~= 0)
  3690.                || (i==2 && inp1 ~= 0 && inp2 ~= 0))
  3691.            {   self.begin_action(action, noun, second, 0);
  3692.                jump turn__end;
  3693.            }
  3694.  
  3695.            !  ...unless a multiple object must be substituted.  First:
  3696.            !  (a) check the multiple list isn't empty;
  3697.            !  (b) warn the player if it has been cut short because too long;
  3698.            !  (c) generate a sequence of actions from the list
  3699.            !      (stopping in the event of death or movement away).
  3700.  
  3701.            multiflag = true;
  3702.            j=multiple_object-->0;
  3703.            if (j==0) { L__M(##Miscellany,2); jump late__error; }
  3704.            if (toomany_flag)
  3705.            {   toomany_flag = false; L__M(##Miscellany,1); }
  3706.            i=location;
  3707.            for (k=1:k<=j:k++)
  3708.            {   if (deadflag) break;
  3709.                if (location ~= i)
  3710.                {   L__M(##Miscellany, 51);
  3711.                    break;
  3712.                }
  3713.                l = multiple_object-->k;
  3714.                PronounNotice(l);
  3715.                print (name) l, ": ";
  3716.                if (inp1 == 0)
  3717.                {   inp1 = l; self.begin_action(action, l, second, 0); inp1 = 0;
  3718.                }
  3719.                else
  3720.                {   inp2 = l; self.begin_action(action, noun, l, 0); inp2 = 0;
  3721.                }
  3722.            }
  3723.  
  3724.            !  --------------------------------------------------------------
  3725.     
  3726.            .turn__end;
  3727.     
  3728.            !  No time passes if either (i) the verb was meta, or
  3729.            !  (ii) we've only had the implicit take before the "real"
  3730.            !  action to follow.
  3731.     
  3732.            if (notheld_mode==1) { NoteObjectAcquisitions(); continue; }
  3733.            if (meta) continue;
  3734.            if (~~deadflag) self.end_turn_sequence();
  3735.        }
  3736.  
  3737.            if (deadflag~=2) AfterLife();
  3738.            if (deadflag==0) jump very__late__error;
  3739.     
  3740.            print "^^    ";
  3741.            #IFV5; style bold; #ENDIF;
  3742.            print "***";
  3743.            if (deadflag==1) L__M(##Miscellany,3);
  3744.            if (deadflag==2) L__M(##Miscellany,4);
  3745.            if (deadflag>2)  { print " "; DeathMessage(); print " "; }
  3746.            print "***";
  3747.            #IFV5; style roman; #ENDIF;
  3748.            print "^^^";
  3749.            ScoreSub();
  3750.            DisplayStatus();
  3751.     
  3752.            .RRQPL;
  3753.            L__M(##Miscellany,5);
  3754.            .RRQL;
  3755.            print "> ";
  3756.            #IFV3; read buffer parse; #ENDIF;
  3757.            temp_global=0;
  3758.            #IFV5; read buffer parse DrawStatusLine; #ENDIF;
  3759.            i=parse-->1;
  3760.            if (i==QUIT1__WD or QUIT2__WD) quit;
  3761.            if (i==RESTART__WD)      @restart;
  3762.            if (i==RESTORE__WD)      { RestoreSub(); jump RRQPL; }
  3763.            if (i==FULLSCORE1__WD or FULLSCORE2__WD && TASKS_PROVIDED==0)
  3764.            {   new_line; FullScoreSub(); jump RRQPL; }
  3765.            if (deadflag==2 && i==AMUSING__WD && AMUSING_PROVIDED==0)
  3766.            {   new_line; Amusing(); jump RRQPL; }
  3767.            #IFV5;
  3768.            if (i==UNDO1__WD or UNDO2__WD or UNDO3__WD)
  3769.            {   if (undo_flag==0)
  3770.                {   L__M(##Miscellany,6);
  3771.                    jump RRQPL;
  3772.                }
  3773.                if (undo_flag==1) jump UndoFailed2;
  3774.                @restore_undo i;
  3775.                if (i==0)
  3776.                {   .UndoFailed2; L__M(##Miscellany,7);
  3777.                }
  3778.                jump RRQPL;
  3779.            }
  3780.            #ENDIF;
  3781.            L__M(##Miscellany,8);
  3782.            jump RRQL;
  3783.        ],
  3784.  
  3785.        end_turn_sequence
  3786.        [ i j;
  3787.  
  3788.            turns++;
  3789.            if (the_time~=NULL)
  3790.            {   if (time_rate>=0) the_time=the_time+time_rate;
  3791.                else
  3792.                {   time_step--;
  3793.                    if (time_step==0)
  3794.                    {   the_time++;
  3795.                        time_step = -time_rate;
  3796.                    }
  3797.                }
  3798.                the_time=the_time % 1440;
  3799.            }
  3800.  
  3801.            #IFDEF DEBUG;
  3802.            if (debug_flag & 4 ~= 0)
  3803.            {   for (i=0: i<active_timers: i++)
  3804.                {   j=the_timers-->i;
  3805.                    if (j~=0)
  3806.                    {   print (name) (j&$7fff), ": ";
  3807.                        if (j & $8000) print "daemon";
  3808.                        else
  3809.                        {   print "timer with ",
  3810.                                  j.time_left, " turns to go"; }
  3811.                        new_line;
  3812.                    }
  3813.                }
  3814.            }
  3815.            #ENDIF;
  3816.  
  3817.            for (i=0: i<active_timers: i++)
  3818.            {   if (deadflag) return;
  3819.                j=the_timers-->i;
  3820.                if (j~=0)
  3821.                {   if (j & $8000) RunRoutines(j&$7fff,daemon);
  3822.                    else
  3823.                    {   if (j.time_left==0)
  3824.                        {   StopTimer(j);
  3825.                            RunRoutines(j,time_out);
  3826.                        }
  3827.                        else
  3828.                            j.time_left=j.time_left-1;
  3829.                    }
  3830.                }
  3831.            }
  3832.            if (deadflag) return;
  3833.  
  3834.            scope_reason=EACH_TURN_REASON; verb_word=0;
  3835.            DoScopeAction(location);
  3836.            SearchScope(ScopeCeiling(player), player, 0);
  3837.            scope_reason=PARSING_REASON;
  3838.  
  3839.            if (deadflag) return;
  3840.  
  3841.            TimePasses();
  3842.  
  3843.            if (deadflag) return;
  3844.  
  3845.            AdjustLight();
  3846.  
  3847.            if (deadflag) return;
  3848.  
  3849.            NoteObjectAcquisitions();
  3850.        ],
  3851.  
  3852.        begin_action
  3853.        [ a n s source   sa sn ss r;
  3854.            sa = action; sn = noun; ss = second;
  3855.            action = a; noun = n; second = s;
  3856.            #IFDEF DEBUG;
  3857.            if (debug_flag & 2 ~= 0) TraceAction(source);
  3858.            #IFNOT;
  3859.            source = 0;
  3860.            #ENDIF;
  3861.            #IFTRUE Grammar__Version == 1;
  3862.            if ((meta || BeforeRoutines()==false) && action<256)
  3863.            {   indirect(#actions_table-->action); r = false;
  3864.            }
  3865.            else r = true;
  3866.            #IFNOT;
  3867.            if ((meta || BeforeRoutines()==false) && action<4096)
  3868.            {   indirect(#actions_table-->action); r = false;
  3869.            }
  3870.            else r = true;
  3871.            #ENDIF;
  3872.            action = sa; noun = sn; second = ss;
  3873.        ],
  3874.   has  proper;
  3875.        
  3876. [ R_Process a i j s1 s2;
  3877.    s1 = inp1; s2 = inp2;
  3878.    inp1 = i; inp2 = j; InformLibrary.begin_action(a, i, j, 1);
  3879.    inp1 = s1; inp2 = s2;
  3880. ];
  3881.  
  3882. [ NoteObjectAcquisitions i;
  3883.   objectloop (i in player && i hasnt moved)
  3884.   {   give i moved;
  3885.       if (i has scored)
  3886.       {   score = score + OBJECT_SCORE;
  3887.           things_score = things_score + OBJECT_SCORE;
  3888.       }
  3889.   }
  3890. ];
  3891.  
  3892. ! ----------------------------------------------------------------------------
  3893.  
  3894. [ TestScope obj act a al sr x y;
  3895.   x=parser_one; y=parser_two;
  3896.   parser_one=obj; parser_two=0; a=actor; al=actors_location;
  3897.   sr=scope_reason; scope_reason=TESTSCOPE_REASON;
  3898.   if (act==0) actor=player; else actor=act;
  3899.   actors_location=ScopeCeiling(actor);
  3900.   SearchScope(actors_location,actor,0); scope_reason=sr; actor=a;
  3901.   actors_location=al; parser_one=x; x=parser_two; parser_two=y;
  3902.   return x;
  3903. ];
  3904.  
  3905. [ LoopOverScope routine act x y a al;
  3906.   x = parser_one; y=scope_reason; a=actor; al=actors_location;
  3907.   parser_one=routine; if (act==0) actor=player; else actor=act;
  3908.   actors_location=ScopeCeiling(actor);
  3909.   scope_reason=LOOPOVERSCOPE_REASON;
  3910.   SearchScope(actors_location,actor,0);
  3911.   parser_one=x; scope_reason=y; actor=a; actors_location=al;
  3912. ];
  3913.  
  3914. [ BeforeRoutines;
  3915.   if (GamePreRoutine()~=0) rtrue;
  3916.   if (RunRoutines(player,orders)~=0) rtrue;
  3917.   if (location~=0 && RunRoutines(location,before)~=0) rtrue;
  3918.   scope_reason=REACT_BEFORE_REASON; parser_one=0;
  3919.   SearchScope(ScopeCeiling(player),player,0); scope_reason=PARSING_REASON;
  3920.   if (parser_one~=0) rtrue;
  3921.   if (inp1>1 && RunRoutines(inp1,before)~=0) rtrue;
  3922.   rfalse;
  3923. ];
  3924.  
  3925. [ AfterRoutines;
  3926.   scope_reason=REACT_AFTER_REASON; parser_one=0;
  3927.   SearchScope(ScopeCeiling(player),player,0); scope_reason=PARSING_REASON;
  3928.   if (parser_one~=0) rtrue;
  3929.   if (location~=0 && RunRoutines(location,after)~=0) rtrue;
  3930.   if (inp1>1 && RunRoutines(inp1,after)~=0) rtrue;
  3931.   return GamePostRoutine();
  3932. ];
  3933.  
  3934. [ RunLife a j;
  3935. #IFDEF DEBUG;
  3936.    if (debug_flag & 2 ~= 0) TraceAction(2, j);
  3937. #ENDIF;
  3938.    reason_code = j; return RunRoutines(a,life);
  3939. ];
  3940.  
  3941. [ ZRegion addr;
  3942.   switch(metaclass(addr))       ! Left over from Inform 5
  3943.   {   nothing: return 0;
  3944.       Object, Class: return 1;
  3945.       Routine: return 2;
  3946.       String: return 3;
  3947.   }
  3948. ];
  3949.  
  3950. [ PrintOrRun obj prop flag;
  3951.   if (obj.#prop > 2) return RunRoutines(obj,prop);
  3952.   if (obj.prop==NULL) rfalse;
  3953.   switch(metaclass(obj.prop))
  3954.   {   Class, Object, nothing: return RunTimeError(2,obj,prop);
  3955.       String: print (string) obj.prop; if (flag==0) new_line; rtrue;
  3956.       Routine: return RunRoutines(obj,prop);
  3957.   }
  3958. ];
  3959.  
  3960. [ ValueOrRun obj prop;
  3961.   if (obj.prop < 256) return obj.prop;
  3962.   return RunRoutines(obj, prop);
  3963. ];
  3964.  
  3965. [ RunRoutines obj prop;
  3966.    if (obj == thedark
  3967.        && prop ~= initial or short_name or description) obj=real_location;
  3968.    if (obj.&prop == 0) rfalse;
  3969.    return obj.prop();
  3970. ];
  3971.  
  3972. [ ChangeDefault prop val;
  3973.    (0-->5)-->(prop-1) = val;
  3974. ];
  3975.  
  3976. ! ----------------------------------------------------------------------------
  3977.  
  3978. [ StartTimer obj timer i;
  3979.    for (i=0:i<active_timers:i++)
  3980.        if (the_timers-->i==obj) rfalse;
  3981.    for (i=0:i<active_timers:i++)
  3982.        if (the_timers-->i==0) jump FoundTSlot;
  3983.    i=active_timers++;
  3984.    if (i*2>=MAX_TIMERS) RunTimeError(4);
  3985.    .FoundTSlot;
  3986.    if (obj.&time_left==0) RunTimeError(5,obj);
  3987.    the_timers-->i=obj; obj.time_left=timer;
  3988. ];
  3989.  
  3990. [ StopTimer obj i;
  3991.    for (i=0:i<active_timers:i++)
  3992.        if (the_timers-->i==obj) jump FoundTSlot2;
  3993.    rfalse;
  3994.    .FoundTSlot2;
  3995.    if (obj.&time_left==0) RunTimeError(5,obj);
  3996.    the_timers-->i=0; obj.time_left=0;
  3997. ];
  3998.  
  3999. [ StartDaemon obj i;
  4000.    for (i=0:i<active_timers:i++)
  4001.        if (the_timers-->i == $8000 + obj)
  4002.            rfalse;
  4003.    for (i=0:i<active_timers:i++)
  4004.        if (the_timers-->i==0) jump FoundTSlot3;
  4005.    i=active_timers++;
  4006.    if (i*2>=MAX_TIMERS) RunTimeError(4);
  4007.    .FoundTSlot3;
  4008.    the_timers-->i = $8000 + obj;
  4009. ];
  4010.  
  4011. [ StopDaemon obj i;
  4012.    for (i=0:i<active_timers:i++)
  4013.        if (the_timers-->i == $8000 + obj) jump FoundTSlot4;
  4014.    rfalse;
  4015.    .FoundTSlot4;
  4016.    the_timers-->i=0;
  4017. ];
  4018.  
  4019. ! ----------------------------------------------------------------------------
  4020.  
  4021. [ DisplayStatus;
  4022.    if (the_time==NULL)
  4023.    {   sline1=score; sline2=turns; }
  4024.    else
  4025.    {   sline1=the_time/60; sline2=the_time%60; }
  4026. ];
  4027.  
  4028. [ SetTime t s;
  4029.    the_time=t; time_rate=s; time_step=0;
  4030.    if (s<0) time_step=0-s;
  4031. ];
  4032.  
  4033. [ NotifyTheScore;
  4034.    print "^[";  L__M(##Miscellany, 50, score-last_score);  print ".]^";
  4035. ];
  4036.  
  4037. ! ----------------------------------------------------------------------------
  4038.  
  4039. [ AdjustLight flag i;
  4040.    i=lightflag;
  4041.    lightflag=OffersLight(parent(player));
  4042.  
  4043.    if (i==0 && lightflag==1)
  4044.    {   location=real_location; if (flag==0) <Look>;
  4045.    }
  4046.  
  4047.    if (i==1 && lightflag==0)
  4048.    {   real_location=location; location=thedark;
  4049.        if (flag==0) { NoteArrival();
  4050.                       return L__M(##Miscellany, 9); }
  4051.    }
  4052.    if (i==0 && lightflag==0) location=thedark;
  4053. ];
  4054.  
  4055. [ OffersLight i j;
  4056.    if (i==0) rfalse;
  4057.    if (i has light) rtrue;
  4058.    objectloop (j in i)
  4059.        if (HasLightSource(j)==1) rtrue;
  4060.    if (i has container)
  4061.    {   if (i has open || i has transparent)
  4062.            return OffersLight(parent(i));
  4063.    }
  4064.    else
  4065.    {   if (i has enterable || i has transparent || i has supporter)
  4066.            return OffersLight(parent(i));
  4067.    }
  4068.    rfalse;
  4069. ];
  4070.  
  4071. [ HasLightSource i j ad;
  4072.    if (i==0) rfalse;
  4073.    if (i has light) rtrue;
  4074.    if (i has enterable || IsSeeThrough(i)==1)
  4075.    {   objectloop (i in i)
  4076.            if (HasLightSource(i)==1) rtrue;
  4077.    }
  4078.    ad = i.&add_to_scope;
  4079.    if (parent(i)~=0 && ad ~= 0)
  4080.    {   if (ad-->0 > top_object)
  4081.        {   ats_hls = 0; ats_flag = 1;
  4082.            RunRoutines(i, add_to_scope);
  4083.            ats_flag = 0; if (ats_hls == 1) rtrue;
  4084.        }
  4085.        else
  4086.        {   for (j=0:(2*j)<i.#add_to_scope:j++)
  4087.                if (HasLightSource(ad-->j)==1) rtrue;
  4088.        }
  4089.    }
  4090.    rfalse;
  4091. ];
  4092.  
  4093. [ ChangePlayer obj flag i;
  4094. !  if (obj.&number==0) return RunTimeError(7,obj);
  4095.   if (actor==player) actor=obj;
  4096.   give player ~transparent ~concealed;
  4097.   i=obj; while(parent(i)~=0) { if (i has animate) give i transparent;
  4098.                                i=parent(i); }
  4099.   if (player==selfobj) player.short_name=FORMER__TX;
  4100.  
  4101.   player=obj;
  4102.  
  4103.   if (player==selfobj) player.short_name=NULL;
  4104.   give player transparent concealed animate proper;
  4105.   i=player; while(parent(i)~=0) i=parent(i); location=i;
  4106.   real_location=location;
  4107.   MoveFloatingObjects();
  4108.   lightflag=OffersLight(parent(player));
  4109.   if (lightflag==0) location=thedark;
  4110.   print_player_flag=flag;
  4111. ];
  4112.  
  4113. ! ----------------------------------------------------------------------------
  4114.  
  4115. #IFDEF DEBUG;
  4116. [ DebugParameter w x n l;
  4117.   x=0-->4; x=x+(x->0)+1; l=x->0; n=(x+1)-->0; x=w-(x+3);
  4118.   print w;
  4119.   if (w>=1 && w<=top_object) print " (", (name) w, ")";
  4120.   if (x%l==0 && (x/l)<n) print " ('", (address) w, "')";
  4121. ];
  4122. [ DebugAction a anames;
  4123. #iftrue Grammar__Version==1;
  4124.   if (a>=256) { print "<fake action ", a-256, ">"; return; }
  4125. #ifnot;
  4126.   if (a>=4096) { print "<fake action ", a-4096, ">"; return; }
  4127. #endif;
  4128.   anames = #identifiers_table;
  4129.   anames = anames + 2*(anames-->0) + 2*48;
  4130.   print (string) anames-->a;
  4131. ];
  4132. [ DebugAttribute a anames;
  4133.   if (a<0 || a>=48) print "<invalid attribute ", a, ">";
  4134.   else
  4135.   {   anames = #identifiers_table; anames = anames + 2*(anames-->0);
  4136.       print (string) anames-->a;
  4137.   }
  4138. ];
  4139. [ TraceAction source ar;
  4140.   if (source<2) print "[ Action ", (DebugAction) action;
  4141.   else
  4142.   {   if (ar==##Order)
  4143.           print "[ Order to ", (name) actor, ": ", (DebugAction) action;
  4144.       else
  4145.           print "[ Life rule ", (DebugAction) ar;
  4146.   }
  4147.   if (noun~=0)   print " with noun ", (DebugParameter) noun;
  4148.   if (second~=0) print " and second ", (DebugParameter) second;
  4149.   if (source==0) print " ";
  4150.   if (source==1) print " (from < > statement) ";
  4151.   print "]^";
  4152. ];
  4153. [ DebugToken token;
  4154.   AnalyseToken(token);
  4155.   switch(found_ttype)
  4156.   {   ILLEGAL_TT: print "<illegal token number ", token, ">";
  4157.       ELEMENTARY_TT:
  4158.       switch(found_tdata)
  4159.       {   NOUN_TOKEN:        print "noun";
  4160.           HELD_TOKEN:        print "held";
  4161.           MULTI_TOKEN:       print "multi";
  4162.           MULTIHELD_TOKEN:   print "multiheld";
  4163.           MULTIEXCEPT_TOKEN: print "multiexcept";
  4164.           MULTIINSIDE_TOKEN: print "multiinside";
  4165.           CREATURE_TOKEN:    print "creature";
  4166.           SPECIAL_TOKEN:     print "special";
  4167.           NUMBER_TOKEN:      print "number";
  4168.           TOPIC_TOKEN:       print "topic";
  4169.           ENDIT_TOKEN:       print "END";
  4170.       }
  4171.       PREPOSITION_TT:
  4172.           print "'", (address) found_tdata, "'";
  4173.       ROUTINE_FILTER_TT:
  4174.           print "noun=Routine(", found_tdata, ")";
  4175.       ATTR_FILTER_TT:
  4176.           print (DebugAttribute) found_tdata;
  4177.       SCOPE_TT:
  4178.           print "scope=Routine(", found_tdata, ")";
  4179.       GPR_TT:
  4180.           print "Routine(", found_tdata, ")";
  4181.   }
  4182. ];
  4183. [ DebugGrammarLine pcount;
  4184.   print " * ";
  4185.   for (:line_token-->pcount ~= ENDIT_TOKEN:pcount++)
  4186.   {   if ((line_token-->pcount)->0 & $10) print "/ ";
  4187.       print (DebugToken) line_token-->pcount, " ";
  4188.   }
  4189.   print "-> ", (DebugAction) action_to_be;
  4190.   if (action_reversed) print " reverse";
  4191. ];
  4192. [ ShowVerbSub address lines da meta i j;
  4193.     if (((noun->#dict_par1) & 1) == 0)
  4194.       "Try typing ~showverb~ and then the name of a verb.";
  4195.     meta=((noun->#dict_par1) & 2)/2;
  4196.     i = $ff-(noun->#dict_par2);
  4197.     address = (0-->7)-->i;
  4198.     lines = address->0;
  4199.     address++;
  4200.     print "Verb ";
  4201.     if (meta) print "meta ";
  4202.     da = 0-->4;
  4203.     for (j=0:j < (da+5)-->0:j++)
  4204.         if (da->(j*9 + 14) == $ff-i)
  4205.             print "'", (address) (da + 9*j + 7), "' ";
  4206.     new_line;
  4207.     if (lines == 0) "has no grammar lines.";
  4208.     for (:lines > 0:lines--)
  4209.     {   address = UnpackGrammarLine(address);
  4210.         print "    "; DebugGrammarLine(); new_line;
  4211.     }
  4212. ];
  4213. [ ShowobjSub c f l a n x;
  4214.    if (noun==0) noun=location;
  4215.    objectloop (c in Class) if (noun ofclass c) { f++; l=c; }
  4216.    new_line;if (f == 1) print (name) l, " ~"; else print "Object ~";
  4217.    print (name) noun, "~ (", noun, ")";
  4218.    if (parent(noun)~=0) print " in ~", (name) parent(noun), "~";
  4219.    new_line;
  4220.    if (f > 1)
  4221.    {   print "  class ";
  4222.        objectloop (c in Class) if (noun ofclass c) print (name) c, " ";
  4223.        new_line;
  4224.    }
  4225.    for (a=0,f=0:a<48:a++) if (noun has a) f=1;
  4226.    if (f)
  4227.    {   print "  has ";
  4228.        for (a=0:a<48:a++) if (noun has a) print (DebugAttribute) a, " ";
  4229.        new_line;
  4230.    }
  4231.    if (noun ofclass Class) return;
  4232.  
  4233.    f=0; l = #identifiers_table-->0;
  4234.    for (a=1:a<=l:a++)
  4235.    {   if ((a~=2 or 3) && noun.&a)
  4236.        {   if (f==0) { print "  with "; f=1; }
  4237.            print (property) a;
  4238.            n = noun.#a;
  4239.            for (c=0:2*c<n:c++)
  4240.            {   print " ";
  4241.                x = (noun.&a)-->c;
  4242.                if (a==name) print "'", (address) x, "'";
  4243.                else
  4244.                {   if (a==number or capacity or time_left)
  4245.                        print x;
  4246.                    else
  4247.                    {   switch(x)
  4248.                        {   NULL: print "NULL";
  4249.                            0: print "0";
  4250.                            1: print "1";
  4251.                            default:
  4252.                            switch(metaclass(x))
  4253.                            {   Class, Object: print (name) x;
  4254.                                String: print "~", (string) x, "~";
  4255.                                Routine: print "[...]";
  4256.                            }
  4257.                            print " (", x, ")";
  4258.                        }
  4259.                    }
  4260.                }
  4261.            }
  4262.            print ",^       ";
  4263.        }
  4264.    }
  4265.    if (f==1) new_line;
  4266. ];
  4267. #ENDIF;
  4268.  
  4269. ! ----------------------------------------------------------------------------
  4270. !  Except in Version 3, the DrawStatusLine routine does just that: this is
  4271. !  provided explicitly so that it can be Replace'd to change the style, and
  4272. !  as written it emulates the ordinary Standard game status line, which is
  4273. !  drawn in hardware
  4274. ! ----------------------------------------------------------------------------
  4275.  
  4276. #IFV5;
  4277. [ DrawStatusLine width posa posb;
  4278.    @split_window 1; @set_window 1; @set_cursor 1 1; style reverse;
  4279.    width = 0->33; posa = width-26; posb = width-13;
  4280.    spaces width;
  4281.    @set_cursor 1 2; print (name) location;
  4282.    if ((0->1)&2 == 0)
  4283.    {   if (width > 76)
  4284.        {   @set_cursor 1 posa; print (string) SCORE__TX, sline1;
  4285.            @set_cursor 1 posb; print (string) MOVES__TX, sline2;
  4286.        }
  4287.        if (width > 63 && width <= 76)
  4288.        {   @set_cursor 1 posb; print sline1, "/", sline2;
  4289.        }
  4290.    }
  4291.    else
  4292.    {   @set_cursor 1 posa;
  4293.        print (string) TIME__TX;
  4294.        LanguageTimeOfDay(sline1, sline2);
  4295.    }
  4296.    @set_cursor 1 1; style roman; @set_window 0;
  4297. ];
  4298. #ENDIF;
  4299.  
  4300. ! ----------------------------------------------------------------------------
  4301. !  Much better menus can be created using the optional library extension
  4302. !  "menus.h".  These are provided for compatibility with previous practice:
  4303. ! ----------------------------------------------------------------------------
  4304.  
  4305. [ LowKey_Menu menu_choices EntryR ChoiceR lines main_title i j;
  4306.   menu_nesting++;
  4307.  .LKRD;
  4308.   menu_item=0;
  4309.   lines=indirect(EntryR);
  4310.   main_title=item_name;
  4311.  
  4312.   print "--- "; print (string) main_title; print " ---^^";
  4313.  
  4314.   if (menu_choices ofclass Routine) menu_choices.call();
  4315.   else print (string) menu_choices;
  4316.  
  4317.   for (::)
  4318.   {   L__M(##Miscellany, 52, lines);
  4319.       print "> ";
  4320.  
  4321.       #IFV3; read buffer parse;
  4322.       #IFNOT; read buffer parse DrawStatusLine;
  4323.       #ENDIF;
  4324.  
  4325.       i=parse-->1;
  4326.       if (i==QUIT1__WD or QUIT2__WD || parse->1==0)
  4327.       {   menu_nesting--; if (menu_nesting>0) rfalse;
  4328.           if (deadflag==0) <<Look>>;
  4329.           rfalse;
  4330.       }
  4331.       i=TryNumber(1);
  4332.       if (i==0) jump LKRD;
  4333.       if (i<1 || i>lines) continue;
  4334.       menu_item=i;
  4335.       j=indirect(ChoiceR);
  4336.       if (j==2) jump LKRD;
  4337.       if (j==3) rfalse;
  4338.   }
  4339. ];
  4340.  
  4341. #IFV3;
  4342. [ DoMenu menu_choices EntryR ChoiceR;
  4343.   LowKey_Menu(menu_choices,EntryR,ChoiceR);
  4344. ];
  4345. #ENDIF;
  4346.  
  4347. #IFV5;
  4348. [ DoMenu menu_choices EntryR ChoiceR
  4349.          lines main_title main_wid cl i j oldcl pkey;
  4350.  
  4351.   if (pretty_flag==0)
  4352.       return LowKey_Menu(menu_choices,EntryR,ChoiceR);
  4353.  
  4354.   menu_nesting++;
  4355.   menu_item=0;
  4356.   lines=indirect(EntryR);
  4357.   main_title=item_name; main_wid=item_width;
  4358.   cl=7;
  4359.  
  4360.   .ReDisplay;
  4361.       oldcl=0;
  4362.       @erase_window $ffff;
  4363.       i=lines+7;
  4364.       @split_window i;
  4365.       i = 0->33;
  4366.       if (i==0) i=80;
  4367.       @set_window 1;
  4368.       @set_cursor 1 1;
  4369.       style reverse;
  4370.       spaces(i); j=i/2-main_wid;
  4371.       @set_cursor 1 j;
  4372.       print (string) main_title;
  4373.       @set_cursor 2 1; spaces(i);
  4374.       @set_cursor 2 2; print (string) NKEY__TX;
  4375.       j=i-12; @set_cursor 2 j; print (string) PKEY__TX;
  4376.       @set_cursor 3 1; spaces(i);
  4377.       @set_cursor 3 2; print (string) RKEY__TX;
  4378.       j=i-17; @set_cursor 3 j;
  4379.       if (menu_nesting==1) print (string) QKEY1__TX;
  4380.                       else print (string) QKEY2__TX;
  4381.       style roman;
  4382.       @set_cursor 5 2; font off;
  4383.  
  4384.       if (menu_choices ofclass String) print (string) menu_choices;
  4385.       else menu_choices.call();
  4386.  
  4387.       for (::)
  4388.       {   if (cl ~= oldcl)
  4389.           {   if (oldcl>0) { @set_cursor oldcl 4; print " "; }
  4390.               @set_cursor cl 4; print ">";
  4391.           }
  4392.           oldcl=cl;
  4393.           @read_char 1 -> pkey;
  4394.           if (pkey==NKEY1__KY or NKEY2__KY or 130)
  4395.           {   cl++; if (cl==7+lines) cl=7; continue;
  4396.           }
  4397.           if (pkey==PKEY1__KY or PKEY2__KY or 129)
  4398.           {   cl--; if (cl==6)  cl=6+lines; continue;
  4399.           }
  4400.           if (pkey==QKEY1__KY or QKEY2__KY or 27 or 131) break;
  4401.           if (pkey==10 or 13 or 132)
  4402.           {   @set_window 0; font on;
  4403.               new_line; new_line; new_line;
  4404.     
  4405.               menu_item=cl-6;
  4406.               EntryR.call();
  4407.     
  4408.               @erase_window $ffff;
  4409.               @split_window 1;
  4410.               i = 0->33; if (i==0) { i=80; }
  4411.               @set_window 1; @set_cursor 1 1; style reverse; spaces(i);
  4412.               j=i/2-item_width;
  4413.               @set_cursor 1 j;
  4414.               print (string) item_name;
  4415.               style roman; @set_window 0; new_line;
  4416.     
  4417.               i = ChoiceR.call();
  4418.               if (i==2) jump ReDisplay;
  4419.               if (i==3) break;
  4420.     
  4421.               L__M(##Miscellany, 53);
  4422.               @read_char 1 -> pkey; jump ReDisplay;
  4423.           }
  4424.       }
  4425.  
  4426.       menu_nesting--; if (menu_nesting>0) rfalse;
  4427.       font on; @set_cursor 1 1;
  4428.       @erase_window $ffff; @set_window 0;
  4429.       new_line; new_line; new_line;
  4430.       if (deadflag==0) <<Look>>;
  4431. ];  
  4432. #ENDIF;
  4433.  
  4434. ! ----------------------------------------------------------------------------
  4435.  
  4436. #ifv5;
  4437. Array StorageForShortName table 160;
  4438. #endif;
  4439.  
  4440. [ PrefaceByArticle o acode pluralise  i artform findout;
  4441.  
  4442.    if (o provides articles)
  4443.    {   print (string) (o.&articles)-->(acode+short_name_case*LanguageCases),
  4444.            " ";
  4445.        if (pluralise) return;
  4446.        print (PSN__) o; return;
  4447.    }
  4448.  
  4449.    i = GetGNAOfObject(o);
  4450.    if (pluralise)
  4451.    {   if (i<3 || (i>=6 && i<9)) i = i + 3;
  4452.    }
  4453.    i = LanguageGNAsToArticles-->i;
  4454.  
  4455.    artform = LanguageArticles
  4456.              + 6*LanguageContractionForms*(short_name_case + i*LanguageCases);
  4457.  
  4458. #iftrue LanguageContractionForms == 2;
  4459.    if (artform-->acode ~= artform-->(acode+3)) findout = true;
  4460. #endif;
  4461. #iftrue LanguageContractionForms == 3;
  4462.    if (artform-->acode ~= artform-->(acode+3)) findout = true;
  4463.    if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
  4464. #endif;
  4465. #iftrue LanguageContractionForms == 4;
  4466.    if (artform-->acode ~= artform-->(acode+3)) findout = true;
  4467.    if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
  4468.    if (artform-->(acode+6) ~= artform-->(acode+9)) findout = true;
  4469. #endif;
  4470. #iftrue LanguageContractionForms > 4;
  4471.    findout = true;
  4472. #endif;
  4473.  
  4474.    if (standard_interpreter ~= 0 && findout)
  4475.    {   StorageForShortName-->0 = 160;
  4476.        @output_stream 3 StorageForShortName;
  4477.        if (pluralise) print (number) pluralise; else print (PSN__) o;
  4478.        @output_stream -3;
  4479.        acode = acode + 3*LanguageContraction(StorageForShortName + 2);
  4480.    }
  4481.  
  4482.    print (string) artform-->acode;
  4483.    if (pluralise) return;
  4484.    print (PSN__) o;
  4485. ];
  4486.  
  4487. [ PSN__ o;
  4488.    if (o==0) { print (string) NOTHING__TX; rtrue; }
  4489.    switch(metaclass(o))
  4490.    {   Routine: print "<routine ", o, ">"; rtrue;
  4491.        String:  print "<string ~", (string) o, "~>"; rtrue;
  4492.        nothing: print "<illegal object number ", o, ">"; rtrue;
  4493.    }
  4494.    if (o==player) { print (string) YOURSELF__TX; rtrue; }
  4495.    #ifdef LanguagePrintShortName;
  4496.    if (LanguagePrintShortName(o)) rtrue;
  4497.    #endif;
  4498.    if (indef_mode && o.&short_name_indef~=0
  4499.        && PrintOrRun(o, short_name_indef, 1)~=0) rtrue;
  4500.    if (o.&short_name~=0 && PrintOrRun(o,short_name,1)~=0) rtrue;
  4501.    @print_obj o;
  4502. ];
  4503.  
  4504. [ Indefart o i;
  4505.    i = indef_mode; indef_mode = true;
  4506.    if (o has proper) { indef_mode = NULL; print (PSN__) o; return; }
  4507.    if (o provides article)
  4508.    {   PrintOrRun(o,article,1); print " ", (PSN__) o; indef_mode = i; return;
  4509.    }
  4510.    PrefaceByArticle(o, 2); indef_mode = i;
  4511. ];
  4512. [ Defart o i;
  4513.    i = indef_mode; indef_mode = false;
  4514.    if (o has proper)
  4515.    { indef_mode = NULL; print (PSN__) o; indef_mode = i; return; }
  4516.    PrefaceByArticle(o, 1); indef_mode = i;
  4517. ];
  4518. [ CDefart o i;
  4519.    i = indef_mode; indef_mode = false;
  4520.    if (o has proper)
  4521.    { indef_mode = NULL; print (PSN__) o; indef_mode = i; return; }
  4522.    PrefaceByArticle(o, 0); indef_mode = i;
  4523. ];
  4524.  
  4525. [ PrintShortName o i;
  4526.    i = indef_mode; indef_mode = NULL;
  4527.    PSN__(o); indef_mode = i;
  4528. ];
  4529.  
  4530. [ EnglishNumber n; LanguageNumber(n); ];
  4531.  
  4532. [ NumberWord o i n;
  4533.   n = LanguageNumbers-->0;
  4534.   for (i=1:i<=n:i=i+2)
  4535.       if (o == LanguageNumbers-->i)
  4536.           return LanguageNumbers-->(i+1);
  4537.   return 0;
  4538. ];
  4539.  
  4540. [ RandomEntry tab;
  4541.   if (tab-->0==0) return RunTimeError(8);
  4542.   return tab-->(random(tab-->0));
  4543. ];
  4544.  
  4545. ! ----------------------------------------------------------------------------
  4546. !  Useful routine: unsigned comparison (for addresses in Z-machine)
  4547. !    Returns 1 if x>y, 0 if x=y, -1 if x<y
  4548. ! ----------------------------------------------------------------------------
  4549.  
  4550. [ UnsignedCompare x y u v;
  4551.   if (x==y) return 0;
  4552.   if (x<0 && y>=0) return 1;
  4553.   if (x>=0 && y<0) return -1;
  4554.   u = x&$7fff; v= y&$7fff;
  4555.   if (u>v) return 1;
  4556.   return -1;
  4557. ];
  4558.  
  4559. ! ----------------------------------------------------------------------------
  4560.  
  4561. [ Banner i;
  4562.    if (Story ~= 0)
  4563.    {
  4564. #IFV5; style bold; #ENDIF;
  4565.    print (string) Story;
  4566. #IFV5; style roman; #ENDIF;
  4567.    }
  4568.    if (Headline ~= 0)
  4569.        print (string) Headline;
  4570.    print "Release ", (0-->1) & $03ff, " / Serial number ";
  4571.    for (i=18:i<24:i++) print (char) 0->i;
  4572.    print " / Inform v"; inversion;
  4573.    print " Library ", (string) LibRelease;
  4574. #ifdef DEBUG;
  4575.    print " D";
  4576. #endif;
  4577.    new_line;
  4578. ];
  4579.  
  4580. [ VersionSub;
  4581.   Banner();
  4582.   if (standard_interpreter > 0)
  4583.       print "Standard interpreter ",
  4584.           standard_interpreter/256, ".", standard_interpreter%256,
  4585.           " (", 0->$1e, (char) 0->$1f, ") / ";
  4586.   else print "Interpreter ", 0->$1e, " Version ", (char) 0->$1f, " / ";
  4587.   print "Library serial number ", (string) LibSerial, "^";
  4588. #IFDEF LanguageVersion;
  4589.   print (string) LanguageVersion, "^";
  4590. #ENDIF;
  4591. ];
  4592.  
  4593. [ RunTimeError n p1 p2;
  4594. #IFDEF DEBUG;
  4595.   print "** Library error ", n, " (", p1, ",", p2, ") **^** ";
  4596.   switch(n)
  4597.   {   1: print "preposition not found (this should not occur)";
  4598.       2: print "Property value not routine or string: ~",
  4599.                (property) p2, "~ of ~", (name) p1, "~ (", p1, ")";
  4600.       3: print "Entry in property list not routine or string: ~",
  4601.                (property) p2, "~ list of ~", (name) p1, "~ (", p1, ")";
  4602.       4: print "Too many timers/daemons are active simultaneously.  The
  4603.                 limit is the library constant MAX_TIMERS (currently ",
  4604.                 MAX_TIMERS, ") and should be increased";
  4605.       5: print "Object ~", (name) p1, "~ has no ~time_left~ property";
  4606.       7: print "The object ~", (name) p1, "~ can only be used as a player
  4607.                 object if it has the ~number~ property";
  4608.       8: print "Attempt to take random entry from an empty table array";
  4609.       9: print p1, " is not a valid direction property number";
  4610.       10: print "The player-object is outside the object tree";
  4611.       11: print "The room ~", (name) p1, "~ has no ~description~ property";
  4612.       12: print "Tried to set a non-existent pronoun using SetPronoun";
  4613.       13: print "A 'topic' token can only be followed by a preposition";
  4614.       default: print "(unexplained)";
  4615.   }
  4616.   " **";
  4617. #IFNOT;
  4618.   "** Library error ", n, " (", p1, ",", p2, ") **";
  4619. #ENDIF;
  4620. ];
  4621.  
  4622. ! ----------------------------------------------------------------------------
  4623.